173 lines
5.0 KiB
Rust
173 lines
5.0 KiB
Rust
use alloc::boxed::Box;
|
|
use alloc::string::String;
|
|
use alloc::vec::Vec;
|
|
use core::any::Any;
|
|
use async_trait::async_trait;
|
|
use crate::std::application::{Application, Error};
|
|
use crate::std::render::{ColorCode, ColouredChar, Dimensions, Frame, Position, RenderError, Window, BUFFER_HEIGHT, BUFFER_WIDTH};
|
|
use crate::std::io::{Color, Display, KeyStroke, Stdin};
|
|
use crate::std::time::Timer;
|
|
use crate::user::lib::libgui::cg_core::CgComponent;
|
|
|
|
pub(crate) struct Game {
|
|
ball: Ball,
|
|
player1: Player,
|
|
player2: Player,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Application for Game {
|
|
fn new(window: Option<Window>) -> Result<Self, Error> {
|
|
Ok(Game {
|
|
ball: Ball::new(),
|
|
player1: Player::new(1),
|
|
player2: Player::new(78),
|
|
})
|
|
}
|
|
|
|
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
|
|
let _d = Display::borrow();
|
|
|
|
let mut update_time = Timer::new(0.1);
|
|
|
|
let mut updated;
|
|
loop {
|
|
updated = false;
|
|
|
|
if let Some(key) = Stdin::try_keystroke() {
|
|
match key {
|
|
KeyStroke::Char('w') => {
|
|
self.player1.move_player(-1);
|
|
updated = true;
|
|
},
|
|
KeyStroke::Char('s') => {
|
|
self.player1.move_player(1);
|
|
updated = true;
|
|
},
|
|
KeyStroke::Up => {
|
|
self.player2.move_player(-1);
|
|
updated = true;
|
|
},
|
|
KeyStroke::Down => {
|
|
self.player2.move_player(1);
|
|
updated = true;
|
|
},
|
|
KeyStroke::Char('`') => break,
|
|
_ => {}
|
|
}
|
|
}
|
|
if update_time.is_done() {
|
|
updated = true;
|
|
self.update_ball();
|
|
update_time.reset()
|
|
}
|
|
|
|
if updated {
|
|
if let Ok(frame) = self.render() {
|
|
frame.write_to_screen().unwrap();
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Game {
|
|
fn update_ball(&mut self) {
|
|
let pos_next_f32 = Position::new( // invert x direction on collision with player
|
|
self.ball.pos.x + self.ball.vx,
|
|
self.ball.pos.y + self.ball.vy,
|
|
);
|
|
|
|
if pos_next_f32.y < 0.0 || pos_next_f32.y >= BUFFER_HEIGHT as f32 { // if the move is outside the screen, then invert the direction
|
|
self.ball.vy = -self.ball.vy;
|
|
}
|
|
|
|
if pos_next_f32.x < 0.0 {
|
|
self.player2.score += 1;
|
|
self.ball.pos = Position::new(40.0, 12.0);
|
|
self.ball.vx = 1.0;
|
|
self.ball.vy = 0.3;
|
|
}
|
|
|
|
if pos_next_f32.x >= BUFFER_WIDTH as f32 {
|
|
self.player1.score += 1;
|
|
self.ball.pos = Position::new(40.0, 12.0);
|
|
self.ball.vx = -1.0;
|
|
self.ball.vy = 0.3;
|
|
}
|
|
|
|
let pos_next = Position::new(
|
|
pos_next_f32.x as usize,
|
|
pos_next_f32.y as usize,
|
|
);
|
|
|
|
for i in 0..5 {
|
|
if self.player1.pos.y + i - 2 == pos_next.y && self.player1.pos.x == pos_next.x {
|
|
self.ball.vx = -self.ball.vx;
|
|
break;
|
|
} else if self.player2.pos.y + i - 2 == pos_next.y && self.player2.pos.x == pos_next.x {
|
|
self.ball.vx = -self.ball.vx;
|
|
break;
|
|
}
|
|
};
|
|
|
|
self.ball.pos = Position::new(
|
|
self.ball.pos.x + self.ball.vx,
|
|
self.ball.pos.y + self.ball.vy
|
|
)
|
|
}
|
|
}
|
|
|
|
impl CgComponent for Game {
|
|
fn render(&self) -> Result<Frame, RenderError> {
|
|
let mut frame = Frame::new(Dimensions::new(0, 0), Dimensions::new(80, 25))?;
|
|
|
|
for y in 0..5 {
|
|
frame.write(Position::new(self.player1.pos.x, self.player1.pos.y + y -2), ColouredChar::coloured('▓', ColorCode::new(Color::Cyan, Color::Black))).unwrap();
|
|
frame.write(Position::new(self.player2.pos.x, self.player2.pos.y + y -2), ColouredChar::coloured('▓', ColorCode::new(Color::Cyan, Color::Black))).unwrap();
|
|
}
|
|
frame.write(self.ball.pos.into_usize().unwrap(), ColouredChar::coloured('O', ColorCode::new(Color::Green, Color::Black))).unwrap();
|
|
|
|
Ok(frame)
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
}
|
|
|
|
struct Player {
|
|
pos: Position<usize>,
|
|
score: i32,
|
|
}
|
|
|
|
|
|
impl Player {
|
|
fn new(x: usize) -> Self {
|
|
Player { pos: Position::new(x, 12), score: 0 }
|
|
}
|
|
|
|
// valid for |y| = 1
|
|
fn move_player(&mut self, y: i32) {
|
|
if self.pos.y < 3 && y < 0 {
|
|
return;
|
|
} else if self.pos.y >= BUFFER_HEIGHT - 3 && y > 0 {
|
|
return;
|
|
}
|
|
self.pos.y = (self.pos.y as i32 + y) as usize;
|
|
}
|
|
}
|
|
|
|
struct Ball {
|
|
pos: Position<f32>,
|
|
vx: f32,
|
|
vy: f32,
|
|
}
|
|
|
|
impl Ball {
|
|
fn new() -> Self {
|
|
Ball { pos: Position::new(40.0, 12.0), vx: 1.0, vy: 0.3 }
|
|
}
|
|
}
|