diff --git a/game/Cargo.toml b/game/Cargo.toml index 553af2ec4fce778e8a9540db206642001e94116c..6f9b9436e769df5dbe7efe110a53b35557b6ef5e 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -7,16 +7,18 @@ edition = "2021" [dependencies] bevy = "0.11.3" -rand = "0.8" + +rand = "0.8.5" + # Enable a small amount of optimization in debug mode -[profile.dev] -opt-level = 1 -syn = "1" +#[profile.dev] +#opt-level = 1 +#syn = "1" [profile.dev.package."*"] opt-level = 3 -[target.x86_64-unknown-linux-gnu] -linker = "clang" -rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"] +#[target.x86_64-unknown-linux-gnu] +#linker = "clang" +#rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"] diff --git a/game/assets/NewMap.png b/game/assets/NewMap.png new file mode 100644 index 0000000000000000000000000000000000000000..a0a863e85c4499d29b1ba175ec3dce03912766f6 Binary files /dev/null and b/game/assets/NewMap.png differ diff --git a/game/assets/battle_background.png b/game/assets/battle_background.png new file mode 100644 index 0000000000000000000000000000000000000000..e26b1bebbc96bd5a311bb04cf4901e0007b40e89 Binary files /dev/null and b/game/assets/battle_background.png differ diff --git a/game/assets/big_char.jpg b/game/assets/big_char.jpg new file mode 100644 index 0000000000000000000000000000000000000000..207be2c10b76cdcb80f18573fabc50b3cb22803b Binary files /dev/null and b/game/assets/big_char.jpg differ diff --git a/game/assets/big_char.png b/game/assets/big_char.png new file mode 100644 index 0000000000000000000000000000000000000000..b99f7c34647477934ebcabd427932f00074aec28 Binary files /dev/null and b/game/assets/big_char.png differ diff --git a/game/assets/boss.png b/game/assets/boss.png new file mode 100644 index 0000000000000000000000000000000000000000..636dc3845ff074ba05546f1b2619aee50eaccfee Binary files /dev/null and b/game/assets/boss.png differ diff --git a/game/assets/character.png b/game/assets/character.png new file mode 100644 index 0000000000000000000000000000000000000000..880252aa0e08198d55d6b27f17e31f12e288aff8 Binary files /dev/null and b/game/assets/character.png differ diff --git a/game/assets/chris.png b/game/assets/chris.png new file mode 100644 index 0000000000000000000000000000000000000000..8381add93db786adb8567ecfc8bcc8535decb08d Binary files /dev/null and b/game/assets/chris.png differ diff --git a/game/assets/chris_char.png b/game/assets/chris_char.png new file mode 100644 index 0000000000000000000000000000000000000000..c1500d2cbe7ea806268902570df2864b1a4e218c Binary files /dev/null and b/game/assets/chris_char.png differ diff --git a/game/assets/cry.png b/game/assets/cry.png new file mode 100644 index 0000000000000000000000000000000000000000..3e759581714c6b7180e696c9988275f1249e5b4e Binary files /dev/null and b/game/assets/cry.png differ diff --git a/game/assets/enemy (1).png b/game/assets/enemy (1).png new file mode 100644 index 0000000000000000000000000000000000000000..880252aa0e08198d55d6b27f17e31f12e288aff8 Binary files /dev/null and b/game/assets/enemy (1).png differ diff --git a/game/assets/fonts/FiraSans-Bold.ttf b/game/assets/fonts/FiraSans-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e3593fb0f3bd412542237820f34fbe74308bfada Binary files /dev/null and b/game/assets/fonts/FiraSans-Bold.ttf differ diff --git a/game/assets/gameover.png b/game/assets/gameover.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f3e8c5d62a32b49dbe2f85a1d7886e495699bc Binary files /dev/null and b/game/assets/gameover.png differ diff --git a/game/assets/hepia.png b/game/assets/hepia.png new file mode 100644 index 0000000000000000000000000000000000000000..69c4a09cfce3bf5be3d07bfbfff5b11628767a1c Binary files /dev/null and b/game/assets/hepia.png differ diff --git a/prof_png/pixil-frame-0 (1).png b/game/assets/malas.png similarity index 100% rename from prof_png/pixil-frame-0 (1).png rename to game/assets/malas.png diff --git a/game/assets/malas_pixel.png b/game/assets/malas_pixel.png new file mode 100644 index 0000000000000000000000000000000000000000..36e20061a6571e619d7c903f5f8453675abd9447 Binary files /dev/null and b/game/assets/malas_pixel.png differ diff --git a/game/assets/malas_pixel_2.png b/game/assets/malas_pixel_2.png new file mode 100644 index 0000000000000000000000000000000000000000..3464e5161d10a7baa7f32dbcbf05f4321db3632b Binary files /dev/null and b/game/assets/malas_pixel_2.png differ diff --git a/game/assets/mapfinal.png b/game/assets/mapfinal.png new file mode 100644 index 0000000000000000000000000000000000000000..4646154eebb9055b20e7f2b312b9ed3538e785bf Binary files /dev/null and b/game/assets/mapfinal.png differ diff --git a/game/assets/paul.jpg b/game/assets/paul.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b200a62b5850a7693ac539aea45c7af43acb5ad Binary files /dev/null and b/game/assets/paul.jpg differ diff --git a/game/assets/paul_pixel.png b/game/assets/paul_pixel.png new file mode 100644 index 0000000000000000000000000000000000000000..5a7612fc3841d243ca24a6602ebdf3666759aa78 Binary files /dev/null and b/game/assets/paul_pixel.png differ diff --git a/game/assets/secret_map.png b/game/assets/secret_map.png new file mode 100644 index 0000000000000000000000000000000000000000..928180b1d6b6ce312503befb9f2449c8356b057e Binary files /dev/null and b/game/assets/secret_map.png differ diff --git a/game/assets/sound/battle_msc.ogg b/game/assets/sound/battle_msc.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ccf4c2e729f429ac106c46f86e1ec6302c94bd5c Binary files /dev/null and b/game/assets/sound/battle_msc.ogg differ diff --git a/game/assets/sound/boss_music.ogg b/game/assets/sound/boss_music.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6957472871481abace2578abf1a41bce00230bde Binary files /dev/null and b/game/assets/sound/boss_music.ogg differ diff --git a/game/assets/sound/combat.mp3 b/game/assets/sound/combat.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..3e313b8ac79e2ee266b7b5a56048c2d03cedac25 Binary files /dev/null and b/game/assets/sound/combat.mp3 differ diff --git a/game/assets/sound/combat.ogg b/game/assets/sound/combat.ogg new file mode 100644 index 0000000000000000000000000000000000000000..01ff18806147fb5bc6355d577e34a0e1555f770b Binary files /dev/null and b/game/assets/sound/combat.ogg differ diff --git a/game/assets/sound/combat.wav b/game/assets/sound/combat.wav new file mode 100644 index 0000000000000000000000000000000000000000..86908f6b10991cba637bbd0c600ccd5356855ea7 Binary files /dev/null and b/game/assets/sound/combat.wav differ diff --git a/game/assets/sound/dev.ogg b/game/assets/sound/dev.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6c282ccf6f6f690aba6dc896f3a53880d2b26f25 Binary files /dev/null and b/game/assets/sound/dev.ogg differ diff --git a/game/assets/sound/gameover.ogg b/game/assets/sound/gameover.ogg new file mode 100644 index 0000000000000000000000000000000000000000..1713da5c8ab0818043e6a9cf13d336a5d7e12f53 Binary files /dev/null and b/game/assets/sound/gameover.ogg differ diff --git a/game/assets/sound/menu.ogg b/game/assets/sound/menu.ogg new file mode 100644 index 0000000000000000000000000000000000000000..c4f84031bf3c938deb0b1110c0fb7fa033fecb1c Binary files /dev/null and b/game/assets/sound/menu.ogg differ diff --git a/game/assets/sound/pokemon.ogg b/game/assets/sound/pokemon.ogg new file mode 100644 index 0000000000000000000000000000000000000000..30639b3afc3d9bac8a746e3b3a9c5145231c7c4b Binary files /dev/null and b/game/assets/sound/pokemon.ogg differ diff --git a/game/assets/sound/victoire.ogg b/game/assets/sound/victoire.ogg new file mode 100644 index 0000000000000000000000000000000000000000..314d7ff760025c6345bcb4bcbd1baa083cd18bb3 Binary files /dev/null and b/game/assets/sound/victoire.ogg differ diff --git a/game/assets/tewfiq_pixel.png b/game/assets/tewfiq_pixel.png new file mode 100644 index 0000000000000000000000000000000000000000..92437d2d9908f943a3f6783a6ff09e8fc1054c69 Binary files /dev/null and b/game/assets/tewfiq_pixel.png differ diff --git a/prof_png/pixil-frame-0 (3).png b/game/assets/toufik.png similarity index 100% rename from prof_png/pixil-frame-0 (3).png rename to game/assets/toufik.png diff --git a/game/assets/trophy.png b/game/assets/trophy.png new file mode 100644 index 0000000000000000000000000000000000000000..e550b80935c5d26f99803cc34722d1420bd55fd5 Binary files /dev/null and b/game/assets/trophy.png differ diff --git a/prof_png/pixil-frame-0 (4).png b/game/assets/upeg.png similarity index 100% rename from prof_png/pixil-frame-0 (4).png rename to game/assets/upeg.png diff --git a/game/assets/upegui_pixel.png b/game/assets/upegui_pixel.png new file mode 100644 index 0000000000000000000000000000000000000000..e1ee5ebe82ed754eac49dcb3bddd5a6122bbcacc Binary files /dev/null and b/game/assets/upegui_pixel.png differ diff --git a/game/src/audio.rs b/game/src/audio.rs new file mode 100644 index 0000000000000000000000000000000000000000..1e331febd302a4eb7eeb62bbf41d94f043b50ee1 --- /dev/null +++ b/game/src/audio.rs @@ -0,0 +1,118 @@ +use bevy::prelude::*; + +use super::{despawn_screen, GameState, Volume, game::ModeGame}; + +pub struct AudioPlugin; + +impl Plugin for AudioPlugin { + fn build(&self, app: &mut App) { + app + .add_systems(OnEnter(GameState::Victory), (despawn_screen::<Audio>,audio_win)) + .add_systems(OnExit(GameState::Victory), despawn_screen::<Audio>) + .add_systems(OnEnter(GameState::GameOver), (despawn_screen::<Audio>,audio_defeat)) + .add_systems(OnExit(GameState::GameOver), despawn_screen::<Audio>) + .add_systems(OnEnter(GameState::Menu), audio_menu) + .add_systems(OnExit(GameState::Menu), despawn_screen::<Audio>) + //.add_systems(OnEnter(GameState::Game), audio_game) + .add_systems(OnEnter(ModeGame::Overworld), audio_game) + .add_systems(OnExit(ModeGame::Overworld), despawn_screen::<Audio>) + .add_systems(OnExit(GameState::Game), despawn_screen::<Audio>) + .add_systems(OnEnter(ModeGame::Fight), (despawn_screen::<Audio>,audio_fight)) + .add_systems(OnExit(ModeGame::Fight), despawn_screen::<Audio>) + .add_systems(OnEnter(ModeGame::Boss), (despawn_screen::<Audio>,audio_boss)) + .add_systems(OnExit(ModeGame::Boss), despawn_screen::<Audio>) + .add_systems(OnEnter(ModeGame::Dev), (despawn_screen::<Audio>,audio_dev)) + .add_systems(Update, audio_volume); + + } +} + +#[derive(Component)] +struct Audio; + +fn audio_win(mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/victoire.ogg"), + ..default() + }, + Audio, + )); +} + +fn audio_defeat (mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/gameover.ogg"), + + ..default() + }, + Audio, + )); +} + +fn audio_menu (mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/menu.ogg"), + ..default() + }, + Audio, + )); +} + +fn audio_game (mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/pokemon.ogg"), + ..default() + }, + Audio, + )); +} + +fn audio_fight (mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/battle_msc.ogg"), + ..default() + }, + Audio, + )); +} + +fn audio_boss (mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/boss_music.ogg"), + ..default() + }, + Audio, + )); +} + +fn audio_dev (mut commands: Commands, asset_server: Res<AssetServer>) { + commands + .spawn(( + AudioBundle { + source: asset_server.load("sound/dev.ogg"), + ..default() + }, + Audio, + )); +} + +fn audio_volume(volume_audio: Res<Volume>, + audio: Query<&AudioSink, With<Audio>>, +) { + let new_volume: f32 = volume_audio.0 as f32; + for entity in &audio { + entity.set_volume(new_volume); + } +} \ No newline at end of file diff --git a/game/src/defeat.rs b/game/src/defeat.rs new file mode 100644 index 0000000000000000000000000000000000000000..5fa3803bec012134b5847d8a1bdeaa46f40f4c81 --- /dev/null +++ b/game/src/defeat.rs @@ -0,0 +1,91 @@ +use bevy::prelude::*; + +use super::{despawn_screen, GameState, TEXT_COLOR,}; + +pub struct GameOverPlugin; + +impl Plugin for GameOverPlugin { + fn build(&self, app: &mut App) { + app + .add_systems(OnEnter(GameState::GameOver), defeat_setup) + .add_systems(Update, countdown.run_if(in_state(GameState::GameOver))) + .add_systems(OnExit(GameState::GameOver),despawn_screen::<OnDefeatScreen>); + } +} + +#[derive(Component)] +struct OnDefeatScreen; + +#[derive(Resource, Deref, DerefMut)] +struct WinTimer(Timer); + +fn defeat_setup(mut commands: Commands, asset_server: Res<AssetServer>) { + let cry_img = asset_server.load("cry.png"); + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnDefeatScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + // Display the Victory + parent.spawn( + TextBundle::from_section( + "GAME OVER\n", + TextStyle { + font_size: 80.0, + color: TEXT_COLOR, + ..default() + }, + ) + .with_style(Style { + margin: UiRect::all(Val::Px(50.0)), + ..default() + }), + ); + }) + .with_children(|parent|{ + parent.spawn( + ImageBundle{ + style: Style { + width: Val::Px(500.0), + ..default() + }, + image: UiImage::new(cry_img), + ..default() + }); + }); + }); + commands.insert_resource(WinTimer(Timer::from_seconds(7.0, TimerMode::Once))); +} + +fn countdown( + mut game_state: ResMut<NextState<GameState>>, + time: Res<Time>, + mut timer: ResMut<WinTimer>, +) { + if timer.tick(time.delta()).finished() { + game_state.set(GameState::Menu); + } +} \ No newline at end of file diff --git a/game/src/game.rs b/game/src/game.rs new file mode 100644 index 0000000000000000000000000000000000000000..02a6d4d4223396b46cadd05f9ab806014ab190c5 --- /dev/null +++ b/game/src/game.rs @@ -0,0 +1,363 @@ +//use std::intrinsics::mir::place; + +//use std::collections::btree_map::Keys; +use bevy::window::PrimaryWindow; +use bevy::prelude::*; +use rand::Rng; +use core::sync::atomic::{AtomicUsize, Ordering}; +//use std::thread::spawn; + +const ENTITY_SIZE: f32 = 50.0; +static ATTACKNO: AtomicUsize = AtomicUsize::new(0); + +use super::{despawn_screen, GameState, TEXT_COLOR, + NORMAL_BUTTON, HOVERED_BUTTON, HOVERED_PRESSED_BUTTON, PRESSED_BUTTON, +}; + +mod menu_speciality; +mod fight; +mod boss; +mod dev; + +pub use self::{ + menu_speciality::*, + fight::*, + boss::*, + dev::*, +}; + + // This plugin will contain the game. + pub struct GamePlugin; + +impl Plugin for GamePlugin { + fn build(&self, app: &mut App) { + app + .add_state::<ModeGame>() + .add_state::<ModeFight>() + .add_systems(OnEnter(GameState::Game), game_setup) + .add_systems(OnEnter(ModeGame::Speciality),spe_menu_setup) + .add_systems(Update, (button_system,menu_spe).run_if(in_state(ModeGame::Speciality))) + .add_systems(OnEnter(ModeGame::Main), (main_game_setup,spawn_enemies,spawn_enemies_v2,despawn_screen::<OnGameScreen>)) + .add_systems(Update,(attack_system,restrain_movement,mouvement,event_overworld).run_if(in_state(ModeGame::Overworld))) + .add_systems(OnEnter(ModeGame::Fight), spawn_combat) + .add_systems(Update, (menu_capacity, combat_outcome).run_if(in_state(ModeGame::Fight))) + .add_systems(OnExit(ModeGame::Fight), ( + despawn_screen::<OnFightScreen>, + despawn_screen::<PlayerText>, + despawn_screen::<EnemyText> + )) + .add_systems(OnEnter(ModeFight::PrepareAttack), update_hp) + .add_systems(OnEnter(ModeFight::PlayerTurn), player_attack) + .add_systems(OnEnter(ModeFight::EnemyTurn), enemy_attack) + .add_systems(OnEnter(ModeGame::Boss), ( + despawn_screen::<Enemy>, + despawn_screen::<OnGameScreen>, + setup_boss, + )) + .add_systems(Update, (attack_system,boss_room_restrain_move,mouvement, boss_event).run_if(in_state(ModeGame::Boss))) + .add_systems(OnEnter(ModeGame::Dev), (despawn_screen::<Enemy>,despawn_screen::<OnGameScreen>,setup_dev)) + .add_systems(Update, (mouvement, dev_room_restrain_move).run_if(in_state(ModeGame::Dev))) + .add_systems(OnExit(GameState::Game), ( + despawn_screen::<Player>, + despawn_screen::<Enemy>, + despawn_screen::<OnGameScreen>, + )); + } +} + + // Tag component used to tag entities added on the game screen + + #[derive(Component)] + struct OnGameScreen; + + #[derive(Component)] + pub struct OnFightScreen; + + #[derive(Component)] + pub struct OnFightBack; + + #[derive(Resource, Deref, DerefMut)] + struct GameTimer(Timer); + + #[derive(Component)] + pub struct Enemy { + pub hp: f32, + pub nbdegat: f32, + pub tag: bool, + img: String, + } + + #[derive(Component)] + struct CollisionBox { + width: f32, + //height: f32, + } + + #[derive(Component)] + enum Speciality { + Logiciel, + Securite, + Embarque, + } + struct Capacites { + pub name: String, + pub degat: f32, + } + + #[derive(Component)] + pub struct Player{ + speed: f32, + moveset: Vec<Capacites>, + hp: f32, + speciality: Speciality, + collision_box: CollisionBox, + } + + #[derive(Component)] + pub struct CombatText; + + #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] + pub enum ModeGame { + Main, + Overworld, + Boss, + Speciality, + Settings, + Fight, + Dev, + #[default] + Disabled, + } + + #[derive(Component)] + struct SelectedOption; + + #[derive(Component)] + pub enum MenuButtonSpe { + LogicielButton, + SecuriteButton, + EmbarqueButton, + } + + #[derive(Component, Copy, Clone)] + pub enum BtnAction { + Cap1, + Cap2, + Cap3, + Cap4, + } + + #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] + pub enum ModeFight{ + #[default] + PrepareAttack, + EnemyTurn, + PlayerTurn, + } + + fn main_game_setup( + mut commands: Commands, + asset_server: Res<AssetServer>, + mut game_state: ResMut<NextState<ModeGame>> + ){ + + let map_texture = asset_server.load("NewMap.png"); + commands + .spawn( + SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(600.0, 600.0)), + ..default() + }, + texture: map_texture, + ..default() + }) + .insert(OnGameScreen); + game_state.set(ModeGame::Overworld); + } + + fn spawn_enemies( + mut commands: Commands, + asset_server: Res<AssetServer>, + ) { + //let enemy_texture = asset_server.load("enemy.png"); + // Définir le nombre maximum d'ennemis + let max_enemies = 5; + + // Générer une position aléatoire pour chaque ennemi + let mut rng = rand::thread_rng(); + for _ in 0..max_enemies { + let x_position = rng.gen_range(-275.0..275.0); // Assurez-vous d'ajuster ces valeurs en fonction de la taille de votre carte + let y_position = rng.gen_range(-275.0..275.0); + + commands.spawn(spawn_one_enemie( + &asset_server, "enemy.png".to_string(),"paul_pixel.png".to_string(), + 200.0, 20.0, x_position, y_position, -1.0)) + .insert(CollisionBox { width: 75.0, /*height: 75.0*/ }); + } + } + + fn spawn_enemies_v2 ( + mut commands: Commands, + asset_server: Res<AssetServer>, + ) { + commands.spawn(spawn_one_enemie( + &asset_server, "upeg.png".to_string(),"upegui_pixel.png".to_string(), + 100.0, 10.0, -275.0, 275.0, 0.0) + ).insert(CollisionBox { width: 75.0, /*height: 75.0*/ }); + + commands.spawn(spawn_one_enemie( + &asset_server, "malas.png".to_string(),"malas_pixel.png".to_string(), + 200.0, 5.0, 275.0, 215.0,0.0) + ).insert(CollisionBox { width: 75.0, /*height: 75.0*/ }); + + commands.spawn(spawn_one_enemie( + &asset_server, "toufik.png".to_string(),"tewfiq_pixel.png".to_string(), + 75.0, 15.0, -275.0, -100.0, 0.0) + ).insert(CollisionBox { width: 75.0, /*height: 75.0*/ }); + } + + fn spawn_one_enemie ( + asset_server: &Res<AssetServer>, + img_chibi: String, + img_battle: String, + max_hp: f32, + max_degat: f32, + x_position: f32, + y_position: f32, + z_position: f32, + ) -> (SpriteBundle, Enemy) { + let enemy_texture = asset_server.load(img_chibi); + let entity = (SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(50.0, 50.0)), + ..default() + }, + transform: Transform { + translation: Vec3::new(x_position, y_position, z_position), + ..default() + }, + texture: enemy_texture.clone(), + ..default() + }, + Enemy { + hp: max_hp, + nbdegat: max_degat, + tag: false, + img: img_battle, + }); + //.insert(CollisionBox { width: 75.0, /*height: 75.0*/ }); + + return entity; + } + + fn mouvement( + mut characters: Query<(&mut Transform, &mut Player)>, + input: Res<Input<KeyCode>>, + time: Res<Time>, + mut game_state: ResMut<NextState<GameState>>, + ) { + for (mut transform, player) in &mut characters { + let mov = player.speed * time.delta_seconds(); + //println!("x: {}, y: {}", transform.translation.x, transform.translation.y); + if input.pressed(KeyCode::W) { + transform.translation.y += mov; + } + if input.pressed(KeyCode::S) { + transform.translation.y -= mov; + } + if input.pressed(KeyCode::D) { + transform.translation.x += mov; + } + if input.pressed(KeyCode::A) { + transform.translation.x -= mov; + } + if input.pressed(KeyCode::Escape) { + game_state.set(GameState::Menu); + } + } + } + + fn attack_system( + mut player_query: Query<&Transform, With<Player>>, + mut enemy_query: Query<(&Transform, &mut Enemy)>, + mut game_state: ResMut<NextState<ModeGame>>, + ) { + // Check if the player is near an enemy to show the combat text + for player_transform in player_query.iter_mut() { + for (enemy_transform, mut enemy) in &mut enemy_query { + if player_transform.translation.distance(enemy_transform.translation) < 60.0 { // Adjust this value based on the size of your collision box + enemy.tag = true; + game_state.set(ModeGame::Fight); + break; + } + } + } + } + + pub fn restrain_movement( + mut characters: Query<(&mut Transform, &Player)>, + //window_query: Query<&mut Window, With<PrimaryWindow>>, + ) { + for (mut transform, player) in &mut characters { + //let window = window_query.get_single().unwrap(); + let half_size = player.collision_box.width / 2.0; + let x_min: f32 = -600.0 / 2.0 + half_size; + let x_max: f32 = 600.0 / 2.0 - half_size; + let y_min: f32 = -600.0 / 2.0 + half_size; + let y_max = 600.0 / 2.0 - half_size; + + let mut translation: Vec3 = transform.translation; + if translation.x < x_min { + translation.x = x_min; + } else if translation.x > x_max { + translation.x = x_max; + } + + if translation.y < y_min { + translation.y = y_min; + } else if translation.y > y_max { + translation.y = y_max; + } + transform.translation = translation; + } + } + + fn button_system( + mut interaction_query: Query< + (&Interaction, &mut BackgroundColor, Option<&SelectedOption>), + (Changed<Interaction>, With<Button>), + >, + ) { + for (interaction, mut color, selected) in &mut interaction_query { + *color = match (*interaction, selected) { + (Interaction::Pressed, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON.into(), + (Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON.into(), + (Interaction::Hovered, None) => HOVERED_BUTTON.into(), + (Interaction::None, None) => NORMAL_BUTTON.into(), + } + } + } + + fn event_overworld ( + mut characters: Query<(&mut Transform, &mut Player)>, + mut mode_state: ResMut<NextState<ModeGame>>, + ) { + for (transform,mut player) in &mut characters { + //Healing zone + if transform.translation.x <= 280.0 && transform.translation.y < 15.0 && transform.translation.y > 5.0 { + player.hp = 100.0; + //println!("healing"); + } + //Boss Room + let nb_capacity = player.moveset.len(); + if transform.translation.x <= 70.0 && transform.translation.x >= -50.0 + && transform.translation.y <= -260.0 && nb_capacity == 4 + { + //println!("Boss"); + mode_state.set(ModeGame::Boss); + } + } + } + + \ No newline at end of file diff --git a/game/src/game/boss.rs b/game/src/game/boss.rs new file mode 100644 index 0000000000000000000000000000000000000000..80614aed2cbe6f754c9e9905828f444d9ba13197 --- /dev/null +++ b/game/src/game/boss.rs @@ -0,0 +1,94 @@ +use bevy::prelude::*; +use super::*; + + +pub fn setup_boss ( + mut commands: Commands, + asset_server: Res<AssetServer>, + mut characters: Query<(&mut Transform, &Player)> +) { + let map_texture = asset_server.load("mapfinal.png"); + commands + .spawn(( + SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(450.0,450.0)), + ..default() + }, + texture: map_texture, + ..default() + }, + OnGameScreen, + )); + + for (mut transform, _player) in &mut characters { + transform.translation.x = 0.0; + transform.translation.y = -170.0; + } + + //let boss_texture = asset_server.load("enemy.png"); + commands + .spawn(( + SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(50.0,50.0)), + ..default() + }, + transform: Transform { + translation: Vec3::new(0.0,200.0,-1.0), + ..default() + }, + //texture: boss_texture.clone(), + ..default() + }, + Enemy { + hp: 50.0, + nbdegat: 400.0, + tag: false, + img: "boss.png".to_string(), + })) + .insert(CollisionBox { width: 75.0, /*height: 75.0*/ }); +} + + +pub fn boss_room_restrain_move ( + mut characters: Query<(&mut Transform, &Player)>, +) { + for (mut transform, player) in &mut characters { + //let window = window_query.get_single().unwrap(); + let half_size = player.collision_box.width / 2.0; + let x_min: f32 = -450.0 / 2.0 + half_size; + let x_max: f32 = 450.0 / 2.0 - half_size; + let y_min: f32 = -450.0 / 2.0 + half_size; + let y_max = 450.0 / 2.0 - half_size; + + let mut translation: Vec3 = transform.translation; + if translation.x < x_min { + translation.x = x_min; + } else if translation.x > x_max { + translation.x = x_max; + } + + if translation.y < y_min { + translation.y = y_min; + } else if translation.y > y_max { + translation.y = y_max; + } + transform.translation = translation; + } +} + +pub fn boss_event ( + mut characters: Query<(&mut Transform, &mut Player)>, + mut mode_state: ResMut<NextState<ModeGame>>, + ) { + for (transform,_player) in &mut characters { + //Dev Room + if transform.translation.x <= -140.0 && transform.translation.x >= -180.0 + && transform.translation.y <= -150.0 + { + //println!("Boss"); + mode_state.set(ModeGame::Dev); + } + } + } diff --git a/game/src/game/dev.rs b/game/src/game/dev.rs new file mode 100644 index 0000000000000000000000000000000000000000..878cfdb6d133b51445c5d74eadc847a1cc807e1e --- /dev/null +++ b/game/src/game/dev.rs @@ -0,0 +1,54 @@ +use bevy::prelude::*; +use super::*; + +pub fn setup_dev ( + mut commands: Commands, + asset_server: Res<AssetServer>, + mut characters: Query<(&mut Transform, &Player)> +) { + let map_texture = asset_server.load("secret_map.png"); + commands + .spawn(( + SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(450.0,450.0)), + ..default() + }, + texture: map_texture, + ..default() + }, + OnGameScreen, + )); + + for (mut transform, _player) in &mut characters { + transform.translation.x = 220.0; + transform.translation.y = 220.0; + } +} + +pub fn dev_room_restrain_move ( + mut characters: Query<(&mut Transform, &Player)>, +) { + for (mut transform, player) in &mut characters { + //let window = window_query.get_single().unwrap(); + let half_size = player.collision_box.width / 2.0; + let x_min: f32 = -450.0 / 2.0 + half_size; + let x_max: f32 = 450.0 / 2.0 - half_size; + let y_min: f32 = -450.0 / 2.0 + half_size; + let y_max = 450.0 / 2.0 - half_size; + + let mut translation: Vec3 = transform.translation; + if translation.x < x_min { + translation.x = x_min; + } else if translation.x > x_max { + translation.x = x_max; + } + + if translation.y < y_min { + translation.y = y_min; + } else if translation.y > y_max { + translation.y = y_max; + } + transform.translation = translation; + } +} diff --git a/game/src/game/fight.rs b/game/src/game/fight.rs new file mode 100644 index 0000000000000000000000000000000000000000..ce7338e7eb05c5b6cd90a149516eff3b2883c148 --- /dev/null +++ b/game/src/game/fight.rs @@ -0,0 +1,418 @@ +use bevy::prelude::*; +use super::*; +use rand::Rng; + +#[derive(Component)] +pub struct PlayerText; + +#[derive(Component)] +pub struct EnemyText; + +pub fn spawn_combat( + mut commands: Commands, + asset_server: Res<AssetServer>, + window_query: Query<&mut Window, With<PrimaryWindow>>, + player_query: Query<&Player>, + enemy_query: Query<&Enemy>, +) { + let players = player_query.get_single(); + let button_style = Style { + width: Val::Px(150.0), + height: Val::Px(65.0), + border: UiRect::all(Val::Px(5.0)), + // horizontally center child text + justify_content: JustifyContent::Center, + // vertically center child text + align_items: AlignItems::Center, + ..default() + }; + + let window = window_query.get_single().unwrap(); + //spawn combat assets + commands.spawn(( + SpriteBundle { + transform: Transform { + translation: Vec3 { + z: 3.0, + ..default() + }, + ..default() + }, + texture: asset_server.load("battle_background.png"), + ..default() + }, + OnFightScreen, + )); + + let mut img_fight = "paul_pixel.png"; + for enemy in &enemy_query { + if enemy.tag == true { + img_fight = &enemy.img; + } + } + + commands.spawn(( + SpriteBundle { + sprite: Sprite { + custom_size: Some(Vec2::new(300.0, 300.0)), + ..default() + }, + transform: Transform { + translation: Vec3 { + x: 280.0, + y: 150.0, + z: 4.0, + }, + ..default() + }, + texture: asset_server.load(img_fight), + ..default() + }, + OnFightScreen, + )); + commands.spawn(( + SpriteBundle { + transform: Transform { + translation: Vec3 { + x: -280.0, + y: -150.0, + z: 4.0, + }, + ..default() + }, + texture: asset_server.load("chris_char.png"), + ..default() + }, + OnFightScreen, + )); + //spawn combat ui + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + align_items: AlignItems::FlexEnd, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnFightScreen, + )) + .with_children(|parent| { + let btnaction: [BtnAction; 4] = [ + BtnAction::Cap1, + BtnAction::Cap2, + BtnAction::Cap3, + BtnAction::Cap4, + ]; + let mut i = 0; + parent + .spawn(NodeBundle { + style: Style { + width: Val::Px(window.width()), + flex_direction: FlexDirection::Row, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + if let Ok(player) = players { + for capacite in &player.moveset { + parent + .spawn(( + ButtonBundle { + style: button_style.clone(), + background_color: NORMAL_BUTTON.into(), + ..default() + }, + btnaction[i], + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + &capacite.name, + TextStyle { ..default() }, + )); + }); + i += 1; + } + } + }) + .with_children(|parent| { + if let Ok(player) = players { + parent.spawn(( + TextBundle { + text: Text { + sections: vec![TextSection { + value: format!("HP Player {}", player.hp), + style: TextStyle { + color: Color::GREEN, + font_size: 30.0, + ..default() + }, + ..default() + }], + ..default() + }, + ..default() + }, + CombatText, + )); + } + }) + .with_children(|parent| { + for enemy in &enemy_query { + if enemy.tag == true { + parent.spawn(( + TextBundle { + text: Text { + sections: vec![TextSection { + value: format!("HP Enemy {}", enemy.hp), + style: TextStyle { + color: Color::RED, + font_size: 30.0, + ..default() + }, + ..default() + }], + ..default() + }, + ..default() + }, + CombatText, + )); + } + } + }); + }); +} + + +pub fn player_attack( + mut enemies: Query<&mut Enemy>, + players: Query<&Player>, + mut combat_state: ResMut<NextState<ModeFight>>, +) { + //println!("entered player_attack()"); + for mut enemy in &mut enemies { + if enemy.tag == true { + for player in &players { + enemy.hp -= player.moveset[ATTACKNO.load(Ordering::Relaxed)].degat; + /*println!( + "enemi prend {} degat", + player.moveset[ATTACKNO.load(Ordering::Relaxed)].degat + );*/ + } + } + } + combat_state.set(ModeFight::EnemyTurn); +} + +pub fn enemy_attack( + enemies: Query<&Enemy>, + mut players: Query<&mut Player>, + mut combat_state: ResMut<NextState<ModeFight>>, +) { + for enemy in &enemies { + if enemy.tag == true { + for mut player in &mut players { + player.hp -= enemy.nbdegat; + //println!("joueur prend {} degats", enemy.nbdegat); + } + } + } + combat_state.set(ModeFight::PrepareAttack); +} + +pub fn menu_capacity( + interaction_query: Query<(&Interaction,&BtnAction),(Changed<Interaction>, With<Button>)>, + mut combat_state: ResMut<NextState<ModeFight>>, +){ + for (interaction, menu_button_action) in &interaction_query { + if *interaction == Interaction::Pressed { + match menu_button_action { + BtnAction::Cap1 => { + ATTACKNO.store(0, Ordering::Relaxed); + //println!("button cap1 pressed"); + combat_state.set(ModeFight::PlayerTurn); + } + BtnAction::Cap2 => { + ATTACKNO.store(1, Ordering::Relaxed); + //println!("button cap2 pressed"); + combat_state.set(ModeFight::PlayerTurn); + } + BtnAction::Cap3 => { + ATTACKNO.store(2, Ordering::Relaxed); + //println!("button cap3 pressed"); + combat_state.set(ModeFight::PlayerTurn); + } + BtnAction::Cap4 => { + ATTACKNO.store(3, Ordering::Relaxed); + //println!("button cap4 pressed"); + combat_state.set(ModeFight::PlayerTurn); + } + } + } + } +} + +pub fn combat_outcome( + mut enemy_query: Query<(Entity, &Transform, &Enemy)>, + mut player_query: Query<(Entity, &Transform, &mut Player)>, + mut game_state: ResMut<NextState<GameState>>, + mut commands: Commands, + mut mode_game: ResMut<NextState<ModeGame>>, +) { + for (enemy_id, _transform, _enemy) in &mut enemy_query { + if _enemy.tag == true { + if _enemy.hp <= 0.0 { + commands.entity(enemy_id).despawn(); + //println!("enemy died"); + if _enemy.img == "paul_pixel.png".to_string(){ + + } else if _enemy.img == "boss.png".to_string() { + mode_game.set(ModeGame::Overworld); + game_state.set(GameState::Victory); + break; + } else { + for (_, _, mut player) in &mut player_query { + let capno: usize = player.moveset.len(); + player.moveset.push(Capacites { + name: format!("Capacite {}", capno.to_string()), + degat: rand::thread_rng().gen_range(0.0..100.0), + }) + } + } + mode_game.set(ModeGame::Overworld); + } + } + } + for (player_id, _transform, _player) in &mut player_query { + if _player.hp <= 0.0 { + commands.entity(player_id).despawn(); + //println!("player died"); + mode_game.set(ModeGame::Main); + game_state.set(GameState::GameOver); + } + } +} + +pub fn update_hp( + enemies: Query<&Enemy>, + players: Query<&Player>, + mut entity_hp: Query<(&mut Text, &CombatText)>, +) { + for (mut ehp, _et) in &mut entity_hp { + for player in &players { + for enemy in &enemies { + if enemy.tag == true { + if ehp.sections[0].style.color == Color::RED { + ehp.sections[0].value = format!("Enemy HP: {}", enemy.hp); + } else { + ehp.sections[0].value = format!("Player HP: {}", player.hp); + } + //println!("enemy hp is {}", enemy.hp); + } + } + } + } +} + +/* +pub fn hp_texte( + mut commands: Commands, + asset_server: Res<AssetServer>, + mut player_query: Query<(&mut Player, &Transform)>, + mut enemy_query: Query<(&mut Enemy, &Transform)>, + mut text_query: Query<Entity, Or<(With<PlayerText>, With<EnemyText>)>> +) { + + for entity in &mut text_query { + commands.entity(entity).despawn_recursive(); + } + // Display Player's HP + + + // Display Enemy's HP + for (enemy, _) in &mut enemy_query { + commands.spawn(( + TextBundle { + transform: Transform { + translation: Vec3{ + x:250.0, + y:-200.0, + z:4.0 + }, + ..default() + }, + style: Style { + margin: UiRect { + left: Val::Percent(90.), + right: Val::Percent(10.), + top: Val::Percent(20.), + ..default() + }, + position_type: PositionType::Absolute, + ..Default::default() + }, + text: Text { + sections: vec![ + TextSection { + value: format!("{} HP Enemy", enemy.hp), + style: TextStyle { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: 30.0, + color: Color::RED, + }, + }, + ], + + ..Default::default() + }, + ..Default::default() + } + .with_text_alignment(TextAlignment::Center), EnemyText)); + } + + for (player, _) in &mut player_query { + commands.spawn(TextBundle { + transform: Transform { + translation: Vec3{ + x:250.0, + y:-200.0, + z:4.0, + }, + ..default() + }, + style: Style { + margin: UiRect { + left: Val::Percent(10.), + right: Val::Percent(90.), + top: Val::Percent(20.), + ..default() + }, + position_type: PositionType::Absolute, + ..Default::default() + }, + text: Text { + sections: vec![ + TextSection { + value: format!("{} HP Player", player.hp), + style: TextStyle { + font: asset_server.load("FiraSans-Bold.ttf"), + font_size: 30.0, + color: Color::GREEN, + }, + }, + ], + ..Default::default() + }, + ..Default::default() + } + .with_text_alignment(TextAlignment::Center)) + .insert(PlayerText); + } +} +*/ diff --git a/game/src/game/menu_speciality.rs b/game/src/game/menu_speciality.rs new file mode 100644 index 0000000000000000000000000000000000000000..0079de7811a7b16386159ec9875b84da20605cc2 --- /dev/null +++ b/game/src/game/menu_speciality.rs @@ -0,0 +1,184 @@ +use bevy::prelude::*; +use super::*; + +pub fn game_setup(mut mode_game: ResMut<NextState<ModeGame>>) { + mode_game.set(ModeGame::Speciality); +} + +pub fn spe_menu_setup( + //mut mode_game: ResMut<NextState<ModeGame>>, + asset_server: Res<AssetServer>, + mut commands: Commands, + //window_query: Query<&mut Window, With<PrimaryWindow>>, +){ + let button_style = Style { + width: Val::Px(250.0), + height: Val::Px(65.0), + margin: UiRect::all(Val::Px(20.0)), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }; + let button_text_style = TextStyle { + font_size: 40.0, + color: TEXT_COLOR, + ..default() + }; + + let texture = asset_server.load("eleve.png"); + commands + .spawn( + SpriteBundle { + texture, + sprite: Sprite { + custom_size: Some(Vec2::new(50.0, 50.0)), + ..default() + }, + transform: Transform { + translation: Vec3 { + z: 2.0, + ..default() + }, + ..default() + }, + ..default() + }, + ) + .insert(Player { + speed: 100.0, + hp: 100.0, + speciality: Speciality::Logiciel, + collision_box: CollisionBox {width: ENTITY_SIZE, /*height: ENTITY_SIZE*/}, + moveset: vec![ + Capacites { + name: "capacite1".to_string(), + degat: 50.0, + }, + ] + }); + + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnGameScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + // Display the Speciality + parent.spawn( + TextBundle::from_section( + "Choisi Ta Speciality !\n", + TextStyle { + font_size: 80.0, + color: TEXT_COLOR, + ..default() + }, + ) + .with_style(Style { + margin: UiRect::all(Val::Px(50.0)), + ..default() + }), + ); + + // Display three buttons for each action available from the main menu: + // - Embarquer + // - Logiciel + // - Securite + parent + .spawn(( + ButtonBundle { + style: button_style.clone(), + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonSpe::LogicielButton, + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + "Logiciel", + button_text_style.clone(), + )); + }); + parent + .spawn(( + ButtonBundle { + style: button_style.clone(), + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonSpe::SecuriteButton, + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + "Security", + button_text_style.clone(), + )); + }); + parent + .spawn(( + ButtonBundle { + style: button_style, + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonSpe::EmbarqueButton, + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + "Embarque", + button_text_style)); + }); + }); + }); +} + +pub fn menu_spe( + interaction_query: Query< + (&Interaction, &MenuButtonSpe), + (Changed<Interaction>, With<Button>), + >, + mut mode_game: ResMut<NextState<ModeGame>>, + mut characters: Query<(&mut Transform, &mut Player)>, +) { + for (interaction, menu_button_action) in &interaction_query { + if *interaction == Interaction::Pressed { + for (_transform, mut player) in &mut characters { + match menu_button_action { + MenuButtonSpe::LogicielButton => { + player.speciality = Speciality::Logiciel; + mode_game.set(ModeGame::Main); + } + MenuButtonSpe::SecuriteButton => { + player.speciality = Speciality::Securite; + mode_game.set(ModeGame::Main); + } + MenuButtonSpe::EmbarqueButton => { + player.speciality = Speciality::Embarque; + mode_game.set(ModeGame::Main); + } + } + } + } + } +} \ No newline at end of file diff --git a/game/src/main.rs b/game/src/main.rs index bd0c5a1f8c64ed98b12053b19a773b588df0b457..fcf074561a9e49435cc996f7b33efd8dc9942cd0 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -1,178 +1,90 @@ -use bevy::prelude::*; -use rand::Rng; +//! This example will display a simple menu using Bevy UI where you can start a new game, +//! change some settings or quit. There is no actual game, it will just display the current +//! settings for 5 seconds before going back to the menu. -#[derive(Component)] -struct Player; +use bevy::prelude::*; -#[derive(Component)] -struct Health { - current: u32, - max: u32, +const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); +const NORMAL_BUTTON: Color = Color::rgb(0.15, 0.15, 0.15); +const HOVERED_BUTTON: Color = Color::rgb(0.25, 0.25, 0.25); +const HOVERED_PRESSED_BUTTON: Color = Color::rgb(0.25, 0.65, 0.25); +const PRESSED_BUTTON: Color = Color::rgb(0.35, 0.75, 0.35); + +// Enum that will be used as a global state for the game +#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] +pub enum GameState { + #[default] + Splash, + Menu, + Game, + GameOver, + Victory, } -#[derive(Component)] -struct CombatText; - - -#[derive(Component)] -struct Attack { - damage: u32, +// One of the two settings that can be set through the menu. It will be a resource in the app +#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] +enum DisplayQuality { + Low, + Medium, + High, } -#[derive(Component)] -struct Enemy; - -#[derive(Component)] -struct CollisionBox { - width: f32, - height: f32, +// Stores the various windows-resolution we can select between. +#[derive(Resource)] +struct ResolutionSettings { + large: Vec2, + medium: Vec2, + small: Vec2, } +// One of the two settings that can be set through the menu. It will be a resource in the app +#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] +struct Volume(u32); + fn main() { App::new() - .add_plugins( - DefaultPlugins - .set(ImagePlugin::default_nearest()) - .set(WindowPlugin { - primary_window: Some(Window { - title: "Logic Farming Rougelike".into(), - resolution: (1024.0, 768.0).into(), - resizable: false, - ..default() - }), - ..default() - }) - .build(), - ) + .insert_resource(ResolutionSettings { + large: Vec2::new(1920.0, 1080.0), + medium: Vec2::new(1200.0, 720.0), + small: Vec2::new(640.0, 360.0), + }) + .add_plugins(DefaultPlugins) + // Insert as resource the initial value for the settings resources + .insert_resource(DisplayQuality::Medium) + .insert_resource(Volume(3)) + // Declare the game state, whose starting value is determined by the `Default` trait + .add_state::<GameState>() .add_systems(Startup, setup) - .add_systems(Update, character_movement) - .add_systems(Startup,spawn_enemies) - .add_systems(Update,attack_system) + // Adds the plugins for each state + .add_plugins(( + splash::SplashPlugin, + menu::MenuPlugin, + game::GamePlugin, + win::WinPlugin, + defeat::GameOverPlugin, + audio::AudioPlugin)) + //.add_plugins(splash::SplashPlugin) .run(); } -fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { - commands.spawn(Camera2dBundle::default()); - - - // Charger l'arrière-plan de la carte - let map_texture = asset_server.load("mapdebut.png"); - commands.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::new(600.0, 600.0)), - ..default() - }, // Assurez-vous d'ajuster la taille selon votre image - texture: map_texture, - ..default() - }); - - - // Charger le sprite de l'élève - let texture = asset_server.load("eleve.png"); - commands.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::new(50.0, 50.0)), - color: Color::WHITE, - flip_x: false, - flip_y: false, - ..default() - }, - texture: texture, - ..default() - }) - .insert(Player); -} - -fn character_movement( - mut characters: Query<(&mut Transform, &Sprite), With<Player>>, - input: Res<Input<KeyCode>>, - time: Res<Time>, -) { - for (mut transform, _) in &mut characters { - if input.pressed(KeyCode::W) { - transform.translation.y += 100.0 * time.delta_seconds(); - } - if input.pressed(KeyCode::S) { - transform.translation.y -= 100.0 * time.delta_seconds(); - } - if input.pressed(KeyCode::D) { - transform.translation.x += 100.0 * time.delta_seconds(); - } - if input.pressed(KeyCode::A) { - transform.translation.x -= 100.0 * time.delta_seconds(); - } - } -} -fn spawn_enemies(mut commands: Commands, asset_server: Res<AssetServer>, mut materials: ResMut<Assets<ColorMaterial>>) { - let enemy_texture = asset_server.load("enemy.png"); +mod splash; +mod game; +mod menu; +mod win; +mod defeat; +mod audio; - // Définir le nombre maximum d'ennemis - let max_enemies = 5; - // Générer une position aléatoire pour chaque ennemi - let mut rng = rand::thread_rng(); - for _ in 0..max_enemies { - let x_position = rng.gen_range(-300.0..300.0); // Assurez-vous d'ajuster ces valeurs en fonction de la taille de votre carte - let y_position = rng.gen_range(-300.0..300.0); - - commands.spawn(SpriteBundle { - sprite: Sprite { - custom_size: Some(Vec2::new(50.0, 50.0)), - ..default() - }, - transform: Transform { - translation: Vec3::new(x_position, y_position, 0.0), - ..default() - }, - texture: enemy_texture.clone(), - ..default() - }) - .insert(Enemy) - //.insert(Health { current: 100, max: 100 }) - //.insert(Attack { damage: 10 }) - .insert(CollisionBox { width: 75.0, height: 75.0 }); - } +fn setup(mut commands: Commands) { + commands.spawn(Camera2dBundle::default()); } -fn attack_system( - mut commands: Commands, - asset_server: Res<AssetServer>, - mut player_query: Query<(&Transform), With<Player>>, - mut enemy_query: Query<(&Transform), With<Enemy>>, - combat_text_query: Query<Entity, With<CombatText>>, // Query to handle existing combat texts -) { - // Check if the player is near an enemy to show the combat text - for (player_transform) in player_query.iter_mut() { - let mut in_combat = false; - for (enemy_transform) in enemy_query.iter_mut() { - if player_transform.translation.distance(enemy_transform.translation) < 60.0 { // Adjust this value based on the size of your collision box - in_combat = true; - break; - } - } - let has_combat_text = combat_text_query.iter().next().is_some(); - if in_combat && !has_combat_text { - // Display the combat text - commands.spawn(TextBundle { - text: Text { - sections: vec![TextSection { - value: "Combat".to_string(), - style: TextStyle { - font: asset_server.load("FiraSans-Bold.ttf"), // Adjust this font path - font_size: 40.0, - color: Color::WHITE, - }, - }], - ..Default::default() - }, - ..Default::default() - }) - .insert(CombatText); - } else if !in_combat && has_combat_text { - // Remove the combat text - for entity in combat_text_query.iter() { - commands.entity(entity).despawn(); - } - } +// Generic system that takes a component as a parameter, and will despawn all entities with that component +fn despawn_screen<T: Component>( + to_despawn: Query<Entity, With<T>>, + mut commands: Commands) +{ + for entity in &to_despawn { + commands.entity(entity).despawn_recursive(); } -} +} \ No newline at end of file diff --git a/game/src/menu.rs b/game/src/menu.rs new file mode 100644 index 0000000000000000000000000000000000000000..bde2e75def29ece95b05efba3ef8448f757102ef --- /dev/null +++ b/game/src/menu.rs @@ -0,0 +1,601 @@ +use bevy::{app::AppExit, prelude::*}; +//use bevy::{prelude::*, window::WindowResized}; +//use bevy::{window::PrimaryWindow}; + + use super::{ + despawn_screen, DisplayQuality, GameState, Volume, TEXT_COLOR, + NORMAL_BUTTON, HOVERED_BUTTON, HOVERED_PRESSED_BUTTON, PRESSED_BUTTON, + }; + use crate::ResolutionSettings; + // This plugin manages the menu, with 5 different screens: + // - a main menu with "New Game", "Settings", "Quit" + // - a settings menu with two submenus and a back button + // - two settings screen with a setting that can be set and a back button + pub struct MenuPlugin; + + impl Plugin for MenuPlugin { + fn build(&self, app: &mut App) { + app + // At start, the menu is not enabled. This will be changed in `menu_setup` when + // entering the `GameState::Menu` state. + // Current screen in the menu is handled by an independent state from `GameState` + .add_state::<MenuState>() + .add_systems(OnEnter(GameState::Menu), menu_setup) + // Systems to handle the main menu screen + .add_systems(OnEnter(MenuState::Main), main_menu_setup) + .add_systems(OnExit(MenuState::Main), despawn_screen::<OnMainMenuScreen>) + // Systems to handle the settings menu screen + .add_systems(OnEnter(MenuState::Settings), settings_menu_setup) + .add_systems( + OnExit(MenuState::Settings), + despawn_screen::<OnSettingsMenuScreen>, + ) + // Systems to handle the display settings screen + .add_systems( + OnEnter(MenuState::SettingsDisplay), + display_settings_menu_setup, + ) + .add_systems( + Update, + ( + setting_button::<DisplayQuality> + .run_if(in_state(MenuState::SettingsDisplay)), + ), + ) + .add_systems( + OnExit(MenuState::SettingsDisplay), + despawn_screen::<OnDisplaySettingsMenuScreen>, + ) + // Systems to handle the sound settings screen + .add_systems(OnEnter(MenuState::SettingsSound), sound_settings_menu_setup) + .add_systems( + Update, + setting_button::<Volume>.run_if(in_state(MenuState::SettingsSound)), + ) + .add_systems( + OnExit(MenuState::SettingsSound), + despawn_screen::<OnSoundSettingsMenuScreen>, + ) + // Common systems to all screens that handles buttons behavior + .add_systems( + Update, + (menu_action, button_system).run_if(in_state(GameState::Menu)), + ) + .add_systems(Update, toggle_resolution); + } + } + + // State used for the current menu screen + #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] + enum MenuState { + Main, + Settings, + SettingsDisplay, + SettingsSound, + #[default] + Disabled, + } + + // Tag component used to tag entities added on the main menu screen + #[derive(Component)] + struct OnMainMenuScreen; + + // Tag component used to tag entities added on the settings menu screen + #[derive(Component)] + struct OnSettingsMenuScreen; + + // Tag component used to tag entities added on the display settings menu screen + #[derive(Component)] + struct OnDisplaySettingsMenuScreen; + + // Tag component used to tag entities added on the sound settings menu screen + #[derive(Component)] + struct OnSoundSettingsMenuScreen; + + // Tag component used to mark which setting is currently selected + #[derive(Component)] + struct SelectedOption; + + #[derive(Component)] + struct AudioMenu; + + // All actions that can be triggered from a button click + #[derive(Component)] + enum MenuButtonAction { + Play, + Settings, + SettingsDisplay, + SettingsSound, + BackToMainMenu, + BackToSettings, + Quit, + } + + // This system handles changing all buttons color based on mouse interaction + fn button_system( + mut interaction_query: Query< + (&Interaction, &mut BackgroundColor, Option<&SelectedOption>), + (Changed<Interaction>, With<Button>), + >, + ) { + for (interaction, mut color, selected) in &mut interaction_query { + *color = match (*interaction, selected) { + (Interaction::Pressed, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON.into(), + (Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON.into(), + (Interaction::Hovered, None) => HOVERED_BUTTON.into(), + (Interaction::None, None) => NORMAL_BUTTON.into(), + } + } + } + + // This system updates the settings when a new value for a setting is selected, and marks + // the button as the one currently selected + fn setting_button<T: Resource + Component + PartialEq + Copy>( + interaction_query: Query<(&Interaction, &T, Entity), (Changed<Interaction>, With<Button>)>, + mut selected_query: Query<(Entity, &mut BackgroundColor), With<SelectedOption>>, + mut commands: Commands, + mut setting: ResMut<T>, + ) { + for (interaction, button_setting, entity) in &interaction_query { + if *interaction == Interaction::Pressed && *setting != *button_setting { + let (previous_button, mut previous_color) = selected_query.single_mut(); + *previous_color = NORMAL_BUTTON.into(); + commands.entity(previous_button).remove::<SelectedOption>(); + commands.entity(entity).insert(SelectedOption); + *setting = *button_setting; + } + } + } + + fn menu_setup(mut menu_state: ResMut<NextState<MenuState>>) { + menu_state.set(MenuState::Main); + } + + fn main_menu_setup( + mut commands: Commands, + asset_server: Res<AssetServer>, + //window_query: Query<&mut Window, With<PrimaryWindow>> + ) { + // Common style for all buttons on the screen + let button_style = Style { + width: Val::Px(250.0), + height: Val::Px(65.0), + margin: UiRect::all(Val::Px(20.0)), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }; + let button_icon_style = Style { + width: Val::Px(30.0), + // This takes the icons out of the flexbox flow, to be positioned exactly + position_type: PositionType::Absolute, + // The icon will be close to the left border of the button + left: Val::Px(10.0), + right: Val::Auto, + ..default() + }; + let button_text_style = TextStyle { + font_size: 40.0, + color: TEXT_COLOR, + ..default() + }; + + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnMainMenuScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + // Display the game name + parent.spawn( + TextBundle::from_section( + "Poke Profs\n", + TextStyle { + font_size: 80.0, + color: TEXT_COLOR, + ..default() + }, + ) + .with_style(Style { + margin: UiRect::all(Val::Px(50.0)), + ..default() + }), + ); + + // Display three buttons for each action available from the main menu: + // - new game + // - settings + // - quit + parent + .spawn(( + ButtonBundle { + style: button_style.clone(), + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonAction::Play, + )) + .with_children(|parent| { + let icon = asset_server.load("textures/Game Icons/right.png"); + parent.spawn(ImageBundle { + style: button_icon_style.clone(), + image: UiImage::new(icon), + ..default() + }); + parent.spawn(TextBundle::from_section( + "New Game", + button_text_style.clone(), + )); + }); + parent + .spawn(( + ButtonBundle { + style: button_style.clone(), + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonAction::Settings, + )) + .with_children(|parent| { + let icon = asset_server.load("textures/Game Icons/wrench.png"); + parent.spawn(ImageBundle { + style: button_icon_style.clone(), + image: UiImage::new(icon), + ..default() + }); + parent.spawn(TextBundle::from_section( + "Settings", + button_text_style.clone(), + )); + }); + parent + .spawn(( + ButtonBundle { + style: button_style, + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonAction::Quit, + )) + .with_children(|parent| { + let icon = asset_server.load("textures/Game Icons/exitRight.png"); + parent.spawn(ImageBundle { + style: button_icon_style, + image: UiImage::new(icon), + ..default() + }); + parent.spawn(TextBundle::from_section("Quit", button_text_style)); + }); + }); + }); + } + + fn settings_menu_setup(mut commands: Commands) { + let button_style = Style { + width: Val::Px(200.0), + height: Val::Px(65.0), + margin: UiRect::all(Val::Px(20.0)), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }; + + let button_text_style = TextStyle { + font_size: 40.0, + color: TEXT_COLOR, + ..default() + }; + + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnSettingsMenuScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + for (action, text) in [ + (MenuButtonAction::SettingsDisplay, "Display"), + (MenuButtonAction::SettingsSound, "Sound"), + (MenuButtonAction::BackToMainMenu, "Back"), + ] { + parent + .spawn(( + ButtonBundle { + style: button_style.clone(), + background_color: NORMAL_BUTTON.into(), + ..default() + }, + action, + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + text, + button_text_style.clone(), + )); + }); + } + }); + }); + } + + fn display_settings_menu_setup(mut commands: Commands, display_quality: Res<DisplayQuality>) { + let button_style = Style { + width: Val::Px(200.0), + height: Val::Px(65.0), + margin: UiRect::all(Val::Px(20.0)), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }; + let button_text_style = TextStyle { + font_size: 40.0, + color: TEXT_COLOR, + ..default() + }; + + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnDisplaySettingsMenuScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + // Create a new `NodeBundle`, this time not setting its `flex_direction`. It will + // use the default value, `FlexDirection::Row`, from left to right. + parent + .spawn(NodeBundle { + style: Style { + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + // Display a label for the current setting + parent.spawn(TextBundle::from_section( + "Display Quality", + button_text_style.clone(), + )); + // Display a button for each possible value + for quality_setting in [ + DisplayQuality::Low, + DisplayQuality::Medium, + DisplayQuality::High, + ] { + let mut entity = parent.spawn(ButtonBundle { + style: Style { + width: Val::Px(150.0), + height: Val::Px(65.0), + ..button_style.clone() + }, + background_color: NORMAL_BUTTON.into(), + ..default() + }); + entity.insert(quality_setting).with_children(|parent| { + parent.spawn(TextBundle::from_section( + format!("{quality_setting:?}"), + button_text_style.clone(), + )); + }); + if *display_quality == quality_setting { + entity.insert(SelectedOption); + } + } + }); + // Display the back button to return to the settings screen + parent + .spawn(( + ButtonBundle { + style: button_style, + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonAction::BackToSettings, + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section("Back", button_text_style)); + }); + }); + }); + } + + fn sound_settings_menu_setup(mut commands: Commands, volume: Res<Volume>) { + let button_style = Style { + width: Val::Px(200.0), + height: Val::Px(65.0), + margin: UiRect::all(Val::Px(20.0)), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }; + let button_text_style = TextStyle { + font_size: 40.0, + color: TEXT_COLOR, + ..default() + }; + + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnSoundSettingsMenuScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + "Volume", + button_text_style.clone(), + )); + for volume_setting in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] { + let mut entity = parent.spawn(ButtonBundle { + style: Style { + width: Val::Px(30.0), + height: Val::Px(65.0), + ..button_style.clone() + }, + background_color: NORMAL_BUTTON.into(), + ..default() + }); + entity.insert(Volume(volume_setting)); + if *volume == Volume(volume_setting) { + entity.insert(SelectedOption); + } + } + }); + parent + .spawn(( + ButtonBundle { + style: button_style, + background_color: NORMAL_BUTTON.into(), + ..default() + }, + MenuButtonAction::BackToSettings, + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section("Back", button_text_style)); + }); + }); + }); + } + + /// This system shows how to request the window to a new resolution + fn toggle_resolution( + keys: Res<Input<KeyCode>>, + mut windows: Query<&mut Window>, + resolution: Res<ResolutionSettings>, + ) { + let mut window = windows.single_mut(); + + if keys.just_pressed(KeyCode::Key1) { + let res = resolution.small; + window.resolution.set(res.x, res.y); + } + if keys.just_pressed(KeyCode::Key2) { + let res = resolution.medium; + window.resolution.set(res.x, res.y); + } + if keys.just_pressed(KeyCode::Key3) { + let res = resolution.large; + window.resolution.set(res.x, res.y); + } + } + + + fn menu_action( + interaction_query: Query< + (&Interaction, &MenuButtonAction), + (Changed<Interaction>, With<Button>), + >, + mut app_exit_events: EventWriter<AppExit>, + mut menu_state: ResMut<NextState<MenuState>>, + mut game_state: ResMut<NextState<GameState>>, + ) { + for (interaction, menu_button_action) in &interaction_query { + if *interaction == Interaction::Pressed { + match menu_button_action { + MenuButtonAction::Quit => app_exit_events.send(AppExit), + MenuButtonAction::Play => { + game_state.set(GameState::Game); + menu_state.set(MenuState::Disabled); + } + MenuButtonAction::Settings => menu_state.set(MenuState::Settings), + MenuButtonAction::SettingsDisplay => { + menu_state.set(MenuState::SettingsDisplay); + } + MenuButtonAction::SettingsSound => { + menu_state.set(MenuState::SettingsSound); + } + MenuButtonAction::BackToMainMenu => menu_state.set(MenuState::Main), + MenuButtonAction::BackToSettings => { + menu_state.set(MenuState::Settings); + } + } + } + } + } \ No newline at end of file diff --git a/game/src/splash.rs b/game/src/splash.rs new file mode 100644 index 0000000000000000000000000000000000000000..bcfe41276e5b05b3f98b86b5f28c87cc3d0a4410 --- /dev/null +++ b/game/src/splash.rs @@ -0,0 +1,69 @@ +use bevy::prelude::*; + + use super::{despawn_screen, GameState}; + + // This plugin will display a splash screen with Bevy logo for 1 second before switching to the menu + pub struct SplashPlugin; + + impl Plugin for SplashPlugin { + fn build(&self, app: &mut App) { + // As this plugin is managing the splash screen, it will focus on the state `GameState::Splash` + app + // When entering the state, spawn everything needed for this screen + .add_systems(OnEnter(GameState::Splash), splash_setup) + // While in this state, run the `countdown` system + .add_systems(Update, countdown.run_if(in_state(GameState::Splash))) + // When exiting the state, despawn everything that was spawned for this screen + .add_systems(OnExit(GameState::Splash), despawn_screen::<OnSplashScreen>); + } + } + + // Tag component used to tag entities added on the splash screen + #[derive(Component)] + struct OnSplashScreen; + + // Newtype to use a `Timer` for this screen as a resource + #[derive(Resource, Deref, DerefMut)] + struct SplashTimer(Timer); + + fn splash_setup(mut commands: Commands, asset_server: Res<AssetServer>) { + let icon = asset_server.load("pokeball.png"); + // Display the logo + commands + .spawn(( + NodeBundle { + style: Style { + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + width: Val::Percent(100.0), + ..default() + }, + ..default() + }, + OnSplashScreen, + )) + .with_children(|parent| { + parent.spawn(ImageBundle { + style: Style { + // This will set the logo to be 200px wide, and auto adjust its height + width: Val::Px(200.0), + ..default() + }, + image: UiImage::new(icon), + ..default() + }); + }); + // Insert the timer as a resource + commands.insert_resource(SplashTimer(Timer::from_seconds(1.0, TimerMode::Once))); + } + + // Tick the timer, and change state when finished + fn countdown( + mut game_state: ResMut<NextState<GameState>>, + time: Res<Time>, + mut timer: ResMut<SplashTimer>, + ) { + if timer.tick(time.delta()).finished() { + game_state.set(GameState::Menu); + } + } \ No newline at end of file diff --git a/game/src/win.rs b/game/src/win.rs new file mode 100644 index 0000000000000000000000000000000000000000..b3791ad074d5515a2fe8a2f3a322d5dad758a9de --- /dev/null +++ b/game/src/win.rs @@ -0,0 +1,102 @@ +use bevy::prelude::*; + +use super::{despawn_screen, GameState, TEXT_COLOR,}; + +pub struct WinPlugin; + +impl Plugin for WinPlugin { + fn build(&self, app: &mut App) { + app + .add_systems(OnEnter(GameState::Victory), win_setup) + .add_systems(Update, countdown.run_if(in_state(GameState::Victory))) + .add_systems(OnExit(GameState::Victory),despawn_screen::<OnWinScreen>); + } +} + +#[derive(Component)] +struct OnWinScreen; + +#[derive(Resource, Deref, DerefMut)] +struct WinTimer(Timer); + +fn win_setup(mut commands: Commands, asset_server: Res<AssetServer>) { + let trophy_img = asset_server.load("trophy.png"); + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + ..default() + }, + ..default() + }, + OnWinScreen, + )) + .with_children(|parent| { + parent + .spawn(NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + ..default() + }, + background_color: Color::BLACK.into(), + ..default() + }) + .with_children(|parent| { + // Display the Victory + parent.spawn( + TextBundle::from_section( + "Victory\n", + TextStyle { + font_size: 80.0, + color: TEXT_COLOR, + ..default() + }, + ) + .with_style(Style { + margin: UiRect::all(Val::Px(50.0)), + ..default() + }), + ); + }) + .with_children(|parent|{ + parent.spawn( + ImageBundle{ + style: Style { + width: Val::Px(200.0), + ..default() + }, + image: UiImage::new(trophy_img), + ..default() + }); + }) + .with_children(|parent|{ + parent.spawn( + ImageBundle{ + style: Style { + width: Val::Px(400.0), + ..default() + }, + image: UiImage::new(asset_server.load("hepia.png")), + ..default() + }); + }); + }); + commands.insert_resource(WinTimer(Timer::from_seconds(10.0, TimerMode::Once))); +} + +fn countdown( + mut game_state: ResMut<NextState<GameState>>, + time: Res<Time>, + mut timer: ResMut<WinTimer>, +) { + if timer.tick(time.delta()).finished() { + game_state.set(GameState::Menu); + } +} \ No newline at end of file diff --git a/game/target/.rustc_info.json b/game/target/.rustc_info.json index 1eec4acb514a284952229f19b13c8ee4cc310a8d..f9037cf12cf63212fe42caebbb2756398062dbae 100644 --- a/game/target/.rustc_info.json +++ b/game/target/.rustc_info.json @@ -1 +1 @@ -{"rustc_fingerprint":11201075139874679237,"outputs":{"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/theo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"14371922958718593042":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/theo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.73.0 (cc66ad468 2023-10-03)\nbinary: rustc\ncommit-hash: cc66ad468955717ab92600c770da8c1601a4ff33\ncommit-date: 2023-10-03\nhost: x86_64-unknown-linux-gnu\nrelease: 1.73.0\nLLVM version: 17.0.2\n","stderr":""}},"successes":{}} \ No newline at end of file +{"rustc_fingerprint":11201075139874679237,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.73.0 (cc66ad468 2023-10-03)\nbinary: rustc\ncommit-hash: cc66ad468955717ab92600c770da8c1601a4ff33\ncommit-date: 2023-10-03\nhost: x86_64-unknown-linux-gnu\nrelease: 1.73.0\nLLVM version: 17.0.2\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/theo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"14371922958718593042":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/theo/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file