Finish project from first half of the book

This commit is contained in:
Fabian Becker 2022-01-29 18:04:52 +01:00
parent 698b11855f
commit f3a84b53cd

View File

@ -1,7 +1,15 @@
use bracket_lib::prelude::*; use bracket_lib::prelude::*;
const SCREEN_WIDTH: i32 = 80;
const SCREEN_HEIGHT: i32 = 50;
const FRAME_DURATION: f32 = 75.0;
struct State { struct State {
player: Player,
frame_time: f32,
obstacle: Obstacle,
mode: GameMode, mode: GameMode,
score: i32,
} }
struct Player { struct Player {
@ -10,19 +18,120 @@ struct Player {
velocity: f32, 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,
size: i32,
}
impl Obstacle {
fn new(x: i32, score: i32) -> Self {
let mut random = RandomNumberGenerator::new();
Obstacle {
x,
gap_y: random.range(10, 40),
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.size / 2;
// Draw the top half of the obstacle
for y in 0..self.gap_y - half_size {
ctx.set(screen_x, y, RED, BLACK, to_cp437('|'));
}
// Draw the lower 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_obstacle(&self, player: &Player) -> bool {
let half_size = self.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)
}
}
impl State { impl State {
fn new() -> State { fn new() -> State {
State { State {
player: Player::new(5, 25),
frame_time: 0.0,
obstacle: Obstacle::new(SCREEN_WIDTH, 0),
mode: GameMode::Menu, mode: GameMode::Menu,
score: 0,
} }
} }
fn play(&mut self, ctx: &mut BTerm) { 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 flap.");
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_obstacle(&self.player) {
self.mode = GameMode::End; self.mode = GameMode::End;
} }
}
fn restart(&mut self) { fn restart(&mut self) {
self.player = Player::new(5, 25);
self.frame_time = 0.0;
self.mode = GameMode::Playing; self.mode = GameMode::Playing;
self.score = 0;
} }
fn main_menu(&mut self, ctx: &mut BTerm) { fn main_menu(&mut self, ctx: &mut BTerm) {
@ -44,6 +153,7 @@ impl State {
ctx.cls(); ctx.cls();
ctx.print_centered(5, "You are dead!"); ctx.print_centered(5, "You are dead!");
ctx.print_centered(6, &format!("You earned {} points", self.score));
ctx.print_centered(8, "(P) Play Game"); ctx.print_centered(8, "(P) Play Game");
ctx.print_centered(9, "(Q) Quit Game"); ctx.print_centered(9, "(Q) Quit Game");
@ -57,6 +167,12 @@ impl State {
} }
} }
impl Default for State {
fn default() -> Self {
Self::new()
}
}
enum GameMode { enum GameMode {
Menu, Menu,
Playing, Playing,