diff --git a/src/main.rs b/src/main.rs index 7f97056..aa9e43c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,16 @@ #![feature(stmt_expr_attributes)] use bracket_lib::prelude::*; +use std::collections::VecDeque; +const SCREEN_WIDTH: u16 = 80; +const SCREEN_HEIGHT: u16 = 50; +const FRAME_DURATION: f32 = 20.0; + +const X_OFFSET: i32 = 10; +const Y_START: i32 = 25; + +#[derive(Debug)] struct Obstacle { x: i32, gap_y: u16, @@ -12,6 +21,7 @@ struct Obstacle { impl Obstacle { fn new(x: i32, score: u32) -> Self { let mut random = RandomNumberGenerator::new(); + // TODO: make smaller with score Obstacle { x, gap_y: random.range(10, 40), @@ -19,6 +29,22 @@ impl Obstacle { gap_height: u16::max(2, 15), } } + + fn render(&mut self, ctx: &mut BTerm, current_x: i32) { + let x = self.x - current_x + X_OFFSET; + let (y_top_of_bottom, y_bottom_of_top) = self.y_bounds(); + let y_positions = [0..y_bottom_of_top, y_top_of_bottom..SCREEN_HEIGHT] + .map(|r| { r.collect::>() }) + .concat(); + for y in y_positions { ctx.set(x, y as i32, RED, BLACK, to_cp437('|')); } + } + + fn y_bounds(&mut self) -> (u16, u16) { + let half_gap_height = self.gap_height / 2; + let y_bottom_of_top = self.gap_y - half_gap_height; + let y_top_of_bottom = self.gap_y + half_gap_height; + (y_top_of_bottom, y_bottom_of_top) + } } struct Player { @@ -27,13 +53,6 @@ struct Player { velocity: f32, } -// const SCREEN_WIDTH: u16 = 80; -const SCREEN_HEIGHT: u16 = 50; -const FRAME_DURATION: f32 = 20.0; - -const X_OFFSET: i32 = 10; -const Y_START: i32 = 25; - impl Player { fn new(y: i32) -> Self { Player { @@ -69,6 +88,8 @@ struct State { player: Player, time: f32, mode: GameMode, + obstacles: VecDeque, + obstacleSpawnTime: f32, score: u32, } @@ -84,6 +105,8 @@ impl State { player: Player::new(Y_START), time: 0.0, mode: GameMode::Menu, + obstacles: VecDeque::new(), + obstacleSpawnTime: 0.0, score: 0u32, } } @@ -107,13 +130,19 @@ impl State { self.player = Player::new(25); self.time = 0.0; self.mode = GameMode::Playing; + self.obstacles = VecDeque::new(); + self.score = 0u32; } fn play(&mut self, ctx: &mut BTerm) { ctx.cls_bg(BLACK); self.time += ctx.frame_time_ms; + self.obstacleSpawnTime -= ctx.frame_time_ms; - ctx.print(1, 1, "Playing..."); + ctx.print(1, 1, "Press Space to flag!"); + ctx.print(1, 2, &format!("Score: {}", self.score)); + ctx.print(1, 3, &format!("Pos: {}, {}", self.player.x, self.player.y)); + ctx.print(1, 4, &format!("Obstables: (Spawn: {}) #{} {:#?}", self.obstacleSpawnTime, self.obstacles.len(), self.obstacles)); // update if self.time > FRAME_DURATION { @@ -121,13 +150,37 @@ impl State { self.time %= FRAME_DURATION; } + if self.obstacleSpawnTime < 0.0 { + let mut random = RandomNumberGenerator::new(); + self.obstacleSpawnTime += random.range(400.0, 1200.0); + let o = Obstacle::new(self.player.x + SCREEN_WIDTH as i32, self.score); + self.obstacles.push_back(o); + } + + if let Some(o) = self.obstacles.front() { + if o.x < self.player.x - X_OFFSET { + self.obstacles.pop_front(); + self.score += 1; + } + } + if let Some(VirtualKeyCode::Space) = ctx.key { self.player.flap(); } self.player.render(ctx); + let mut did_collide = false; + self.obstacles.iter_mut().for_each(|o| { + if o.x == self.player.x { + let (y_top_of_bottom, y_bottom_of_top) = o.y_bounds(); + if (self.player.y as u16) >= y_top_of_bottom || (self.player.y as u16) <= y_bottom_of_top { + did_collide = true; + } + } + o.render(ctx, self.player.x); + }); - if self.player.y > SCREEN_HEIGHT as i32 { + if did_collide || self.player.y > (SCREEN_HEIGHT as i32) { self.die(); } // self.mode = GameMode::Dead; @@ -140,6 +193,7 @@ impl State { fn dead(&mut self, ctx: &mut BTerm) { ctx.cls(); ctx.print_centered(5, "You are dead. =("); + ctx.print_centered(6, &format!("Final Score: {}", self.score)); ctx.print_centered(8, "(P) Play Again"); ctx.print_centered(9, "(Q) Quit");