From 7649edf43909f10172bf271b6f533ef280a11085 Mon Sep 17 00:00:00 2001 From: unintendedfraud Date: Fri, 6 Oct 2023 19:06:25 +0200 Subject: [PATCH] flappy dragons --- flappy/Cargo.toml | 9 +++ flappy/src/main.rs | 196 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 flappy/Cargo.toml create mode 100644 flappy/src/main.rs diff --git a/flappy/Cargo.toml b/flappy/Cargo.toml new file mode 100644 index 0000000..1a0bf2c --- /dev/null +++ b/flappy/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "flappy" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bracket-lib = "~0.8.1" diff --git a/flappy/src/main.rs b/flappy/src/main.rs new file mode 100644 index 0000000..0fd0c2c --- /dev/null +++ b/flappy/src/main.rs @@ -0,0 +1,196 @@ +use bracket_lib::prelude::*; + +const SCREEN_WIDTH: i32 = 80; +const SCREEN_HEIGHT: i32 = 50; +const FRAME_DURATION: f32 = 75.0; + +enum GameMode { + Menu, + Playing, + End, +} + +struct State { + mode: GameMode, + player: Player, + frame_time: f32, + score: i32, + obstacle: Obstacle, +} + +impl State { + fn new() -> Self { + State { + mode: GameMode::Menu, + frame_time: 0.0, + player: Player::new(5, 25), + score: 0, + obstacle: Obstacle::new(SCREEN_WIDTH, 0), + } + } + + fn dead(&mut self, ctx: &mut BTerm) { + ctx.cls(); + ctx.print_centered(8, "You are dead !!"); + ctx.print_centered(9, &format!("You passed {} obstacles", self.score)); + ctx.print_centered(11, "(P) Play again"); + ctx.print_centered(13, "(Q) Quit"); + + if let Some(key) = ctx.key { + match key { + VirtualKeyCode::P => self.restart(), + VirtualKeyCode::Q => ctx.quitting = true, + _ => {} + } + } + } + + fn play(&mut self, ctx: &mut BTerm) { + ctx.cls_bg(NAVY); + self.frame_time += ctx.frame_time_ms; + if self.frame_time > FRAME_DURATION { + self.frame_time = 0.0; + self.player.gravity_and_move(); + } + + if let Some(VirtualKeyCode::Space) = ctx.key { + self.player.flap(); + } + + self.player.render(ctx); + + ctx.print(0, 0, "Press SPACE to fly"); + ctx.print(0, 1, &format!("Score: {}", self.score)); + + self.obstacle.render(ctx, self.player.x); + + if self.player.x > self.obstacle.x { + self.score += 1; + self.obstacle = Obstacle::new(self.player.x + SCREEN_WIDTH, self.score); + } + + if self.player.y > SCREEN_HEIGHT || self.obstacle.hit_player(&self.player) { + self.mode = GameMode::End; + } + } + + fn restart(&mut self) { + self.player = Player::new(5, 25); + self.frame_time = 0.0; + self.mode = GameMode::Playing; + self.score = 0; + self.obstacle = Obstacle::new(SCREEN_WIDTH, 0); + } + + fn main_menu(&mut self, ctx: &mut BTerm) { + ctx.cls(); + ctx.print_centered(8, "Welcome to Flappy Dragons !"); + ctx.print_centered(10, "(P) Play"); + ctx.print_centered(12, "(Q) Quit"); + + if let Some(key) = ctx.key { + match key { + VirtualKeyCode::P => self.restart(), + VirtualKeyCode::Q => ctx.quitting = true, + _ => {} + } + } + } +} + +impl GameState for State { + fn tick(&mut self, ctx: &mut BTerm) { + match self.mode { + GameMode::Menu => self.main_menu(ctx), + GameMode::End => self.dead(ctx), + GameMode::Playing => self.play(ctx), + } + } +} + +struct Player { + x: i32, + y: i32, + velocity: f32, +} + +impl Player { + fn new(x: i32, y: i32) -> Self { + Player { + x, + y, + velocity: 0.0, + } + } + + fn render(&mut self, ctx: &mut BTerm) { + ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@')); + } + + fn gravity_and_move(&mut self) { + if self.velocity < 2.0 { + self.velocity += 0.2; + } + + self.y += self.velocity as i32; + self.x += 1; + + if self.y < 0 { + self.y = 0; + } + } + + fn flap(&mut self) { + self.velocity = -2.0; + } +} + +struct Obstacle { + x: i32, + gap_y: i32, + gap_size: i32, +} + +impl Obstacle { + fn new(x: i32, score: i32) -> Self { + let mut random = RandomNumberGenerator::new(); + + Obstacle { + x, + gap_y: random.range(10, 40), + gap_size: i32::max(2, 20 - score), + } + } + + fn render(&mut self, ctx: &mut BTerm, player_x: i32) { + let screen_x = self.x - player_x; + let half_size = self.gap_size / 2; + + // Top half of the obstacle + for y in 0..self.gap_y - half_size { + ctx.set(screen_x, y, RED, BLACK, to_cp437('|')) + } + + // Bottom half of the obstacle + for y in self.gap_y + half_size..SCREEN_HEIGHT { + ctx.set(screen_x, y, RED, BLACK, to_cp437('|')) + } + } + + fn hit_player(&mut self, player: &Player) -> bool { + let half_size = self.gap_size / 2; + let does_x_match = player.x == self.x; + let player_above_gap = player.y < self.gap_y - half_size; + let player_below_gap = player.y > self.gap_y + half_size; + + does_x_match && (player_above_gap || player_below_gap) + } +} + +fn main() -> BError { + let ctx = BTermBuilder::simple80x50() + .with_title("Flappy Dragon") + .build()?; + + main_loop(ctx, State::new()) +}