From 791cb424cec24720cf851b2c2faf34d9d6d6bc9e Mon Sep 17 00:00:00 2001 From: Kostinatyn Konakhevych Date: Tue, 7 May 2024 17:37:13 +0300 Subject: [PATCH 1/3] Second player mode. Plus update reset feature. --- src/frame.rs | 4 ++++ src/invaders.rs | 7 +++++++ src/level.rs | 8 +++++++- src/main.rs | 40 ++++++++++++++++++++++++++++++---------- src/menu.rs | 4 +++- src/player.rs | 18 +++++++++++++----- src/score.rs | 8 +++++++- 7 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/frame.rs b/src/frame.rs index 05501e2..74992f8 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -9,3 +9,7 @@ pub fn new_frame() -> Frame { pub trait Drawable { fn draw(&self, frame: &mut Frame); } + +pub trait Reset { + fn reset(&mut self); +} \ No newline at end of file diff --git a/src/invaders.rs b/src/invaders.rs index dca4c1b..8405afa 100644 --- a/src/invaders.rs +++ b/src/invaders.rs @@ -4,6 +4,7 @@ use crate::{ }; use rusty_time::Timer; use std::{cmp::max, time::Duration}; +use crate::frame::Reset; pub struct Invader { pub x: usize, @@ -102,6 +103,12 @@ impl Default for Invaders { } } +impl Reset for Invaders { + fn reset(&mut self) { + *self = Invaders::new(); + } +} + impl Drawable for Invaders { fn draw(&self, frame: &mut Frame) { for invader in self.army.iter() { diff --git a/src/level.rs b/src/level.rs index 17f781f..4356345 100644 --- a/src/level.rs +++ b/src/level.rs @@ -1,4 +1,4 @@ -use crate::frame::{Drawable, Frame}; +use crate::frame::{Drawable, Frame, Reset}; const MAX_LEVEL: u8 = 3; @@ -25,6 +25,12 @@ impl Default for Level { } } +impl Reset for Level { + fn reset(&mut self) { + *self = Level::default(); + } +} + impl Drawable for Level { fn draw(&self, frame: &mut Frame) { // format our level string diff --git a/src/main.rs b/src/main.rs index 52ed8a2..078d2e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use std::{ }; use invaders::{ - frame::{self, new_frame, Drawable, Frame}, + frame::{new_frame, Drawable, Reset, Frame}, invaders::Invaders, level::Level, menu::Menu, @@ -23,7 +23,7 @@ use invaders::{ }; fn render_screen(render_rx: Receiver) { - let mut last_frame = frame::new_frame(); + let mut last_frame = new_frame(); let mut stdout = io::stdout(); render::render(&mut stdout, &last_frame, &last_frame, true); while let Ok(curr_frame) = render_rx.recv() { @@ -32,10 +32,11 @@ fn render_screen(render_rx: Receiver) { } } -fn reset_game(in_menu: &mut bool, player: &mut Player, invaders: &mut Invaders) { +fn reset_game(in_menu: &mut bool, to_reset: &mut Vec<&mut dyn Reset>) { *in_menu = true; - *player = Player::new(); - *invaders = Invaders::new(); + for elem in to_reset { + elem.reset(); + } } fn main() -> Result<(), Box> { @@ -58,13 +59,15 @@ fn main() -> Result<(), Box> { }); // Game loop - let mut player = Player::new(); + let mut player = Player::new('A'); + let mut player2 = Player::new('Q'); let mut instant = Instant::now(); let mut invaders = Invaders::new(); let mut score = Score::new(); let mut menu = Menu::new(); let mut in_menu = true; let mut level = Level::new(); + let mut player2_mode = false; 'gameloop: loop { // Per-frame init @@ -82,6 +85,10 @@ fn main() -> Result<(), Box> { KeyCode::Char(' ') | KeyCode::Enter => { if menu.selection == 0 { in_menu = false; + } + else if menu.selection == 1 { + in_menu = false; + player2_mode = true; } else { break 'gameloop; } @@ -108,9 +115,17 @@ fn main() -> Result<(), Box> { audio.play("pew"); } } + KeyCode::Char('a') => player2.move_left(), + KeyCode::Char('d') => player2.move_right(), + KeyCode::Char('w') => { + if player2.shoot() { + audio.play("pew"); + } + } KeyCode::Esc | KeyCode::Char('q') => { audio.play("lose"); - reset_game(&mut in_menu, &mut player, &mut invaders); + let mut to_reset: Vec<&mut dyn Reset> = vec![&mut player, &mut player2, &mut invaders, &mut score, &mut level]; + reset_game(&mut in_menu, &mut to_reset); } _ => {} } @@ -119,17 +134,21 @@ fn main() -> Result<(), Box> { // Updates player.update(delta); + if player2_mode { player2.update(delta); } + if invaders.update(delta) { audio.play("move"); } - let hits: u16 = player.detect_hits(&mut invaders); + let mut hits: u16 = player.detect_hits(&mut invaders); + hits += player2.detect_hits(&mut invaders); if hits > 0 { audio.play("explode"); score.add_points(hits); } // Draw & render - let drawables: Vec<&dyn Drawable> = vec![&player, &invaders, &score, &level]; + let mut drawables: Vec<&dyn Drawable> = vec![&player, &invaders, &score, &level]; + if player2_mode { drawables.push(&player2); } for drawable in drawables { drawable.draw(&mut curr_frame); } @@ -145,7 +164,8 @@ fn main() -> Result<(), Box> { invaders = Invaders::new(); } else if invaders.reached_bottom() { audio.play("lose"); - reset_game(&mut in_menu, &mut player, &mut invaders); + let mut to_reset: Vec<&mut dyn Reset> = vec![&mut player, &mut player2, &mut invaders, &mut score, &mut level]; + reset_game(&mut in_menu, &mut to_reset); } } diff --git a/src/menu.rs b/src/menu.rs index 5af9b60..58e92d9 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -8,7 +8,9 @@ pub struct Menu { impl Menu { pub fn new() -> Self { Self { - options: vec![String::from("New game"), String::from("Exit")], + options: vec![String::from("New game"), + String::from("2 players"), + String::from("Exit")], selection: 0, } } diff --git a/src/player.rs b/src/player.rs index 8bd6b28..4c92410 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,5 +1,5 @@ use crate::{ - frame::{Drawable, Frame}, + frame::{Drawable, Reset, Frame}, invaders::Invaders, shot::Shot, {NUM_COLS, NUM_ROWS}, @@ -10,14 +10,16 @@ pub struct Player { x: usize, y: usize, shots: Vec, + view: char, } impl Player { - pub fn new() -> Self { + pub fn new(view: char) -> Self { Self { x: NUM_COLS / 2, y: NUM_ROWS - 1, shots: Vec::new(), + view: view } } pub fn move_left(&mut self) { @@ -61,15 +63,21 @@ impl Player { impl Default for Player { fn default() -> Self { - Self::new() + Self::new('A') + } +} + +impl Reset for Player { + fn reset(&mut self) { + *self = Player::new(self.view); } } impl Drawable for Player { fn draw(&self, frame: &mut Frame) { - frame[self.x][self.y] = 'A'; + frame[self.x][self.y] = self.view; for shot in self.shots.iter() { shot.draw(frame); } } -} +} \ No newline at end of file diff --git a/src/score.rs b/src/score.rs index 9c9979e..96aefe2 100644 --- a/src/score.rs +++ b/src/score.rs @@ -1,4 +1,4 @@ -use crate::frame::{Drawable, Frame}; +use crate::frame::{Drawable, Frame, Reset}; #[derive(Default)] pub struct Score { @@ -15,6 +15,12 @@ impl Score { } } +impl Reset for Score { + fn reset(&mut self) { + *self = Score::default(); + } +} + impl Drawable for Score { fn draw(&self, frame: &mut Frame) { // format our score string From 084979a180de106b00676cfb10f44b38dc0cb98c Mon Sep 17 00:00:00 2001 From: Kostinatyn Konakhevych Date: Tue, 7 May 2024 19:28:32 +0300 Subject: [PATCH 2/3] added 2 player on fly --- src/level.rs | 2 +- src/main.rs | 30 ++++++++++++++++++------------ src/player.rs | 21 ++++++++++++++++++++- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/level.rs b/src/level.rs index 4356345..9462f73 100644 --- a/src/level.rs +++ b/src/level.rs @@ -39,7 +39,7 @@ impl Drawable for Level { // iterate over all characters for (i, c) in formatted.chars().enumerate() { // put them in the first row - frame[i + 20][0] = c; + frame[i + 13][0] = c; } } } diff --git a/src/main.rs b/src/main.rs index 078d2e8..856c964 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use invaders::{ invaders::Invaders, level::Level, menu::Menu, - player::Player, + player::{Player, Player2Mode}, render, score::Score, }; @@ -32,8 +32,9 @@ fn render_screen(render_rx: Receiver) { } } -fn reset_game(in_menu: &mut bool, to_reset: &mut Vec<&mut dyn Reset>) { +fn reset_game(in_menu: &mut bool, player2_mode: &mut Player2Mode, to_reset: &mut Vec<&mut dyn Reset>) { *in_menu = true; + *player2_mode = Player2Mode::Enabled(false); for elem in to_reset { elem.reset(); } @@ -67,7 +68,7 @@ fn main() -> Result<(), Box> { let mut menu = Menu::new(); let mut in_menu = true; let mut level = Level::new(); - let mut player2_mode = false; + let mut player2_mode = Player2Mode::Enabled(false); 'gameloop: loop { // Per-frame init @@ -88,7 +89,7 @@ fn main() -> Result<(), Box> { } else if menu.selection == 1 { in_menu = false; - player2_mode = true; + player2_mode = Player2Mode::Enabled(true); } else { break 'gameloop; } @@ -114,19 +115,22 @@ fn main() -> Result<(), Box> { if player.shoot() { audio.play("pew"); } - } + }, KeyCode::Char('a') => player2.move_left(), KeyCode::Char('d') => player2.move_right(), KeyCode::Char('w') => { if player2.shoot() { audio.play("pew"); } - } + }, KeyCode::Esc | KeyCode::Char('q') => { audio.play("lose"); let mut to_reset: Vec<&mut dyn Reset> = vec![&mut player, &mut player2, &mut invaders, &mut score, &mut level]; - reset_game(&mut in_menu, &mut to_reset); - } + reset_game(&mut in_menu, &mut player2_mode, &mut to_reset); + }, + KeyCode::Char('e') => { + player2_mode = Player2Mode::Enabled(true); + }, _ => {} } } @@ -134,7 +138,7 @@ fn main() -> Result<(), Box> { // Updates player.update(delta); - if player2_mode { player2.update(delta); } + if player2_mode == Player2Mode::Enabled(true) { player2.update(delta); } if invaders.update(delta) { audio.play("move"); @@ -147,8 +151,10 @@ fn main() -> Result<(), Box> { } // Draw & render - let mut drawables: Vec<&dyn Drawable> = vec![&player, &invaders, &score, &level]; - if player2_mode { drawables.push(&player2); } + let mut drawables: Vec<&dyn Drawable> = vec![&player, &invaders, &score, &level, &player2_mode]; + if player2_mode == Player2Mode::Enabled(true) { + drawables.push(&player2); + } for drawable in drawables { drawable.draw(&mut curr_frame); } @@ -165,7 +171,7 @@ fn main() -> Result<(), Box> { } else if invaders.reached_bottom() { audio.play("lose"); let mut to_reset: Vec<&mut dyn Reset> = vec![&mut player, &mut player2, &mut invaders, &mut score, &mut level]; - reset_game(&mut in_menu, &mut to_reset); + reset_game(&mut in_menu, &mut player2_mode, &mut to_reset); } } diff --git a/src/player.rs b/src/player.rs index 4c92410..a88e268 100644 --- a/src/player.rs +++ b/src/player.rs @@ -80,4 +80,23 @@ impl Drawable for Player { shot.draw(frame); } } -} \ No newline at end of file +} +#[derive(PartialEq, Debug)] +pub enum Player2Mode { + Enabled(bool), +} + +impl Drawable for Player2Mode { + fn draw(&self, frame: &mut Frame) { + // format our player2 string + if *self == Player2Mode::Enabled(false) { + let formatted = format!("P2: PRESS E"); + + // iterate over all characters + for (i, c) in formatted.chars().enumerate() { + // put them in the first row + frame[i + 23][0] = c; + } + } + } +} From abdb1b3b8b464bd44b6dc2ecf024d1120079e242 Mon Sep 17 00:00:00 2001 From: Kostinatyn Konakhevych Date: Sun, 16 Jun 2024 01:04:03 +0300 Subject: [PATCH 3/3] Add snake game to ReadMe --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cc9f03d..8238142 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Were you inspired to make your own terminal-based game? Open a PR to add it to t * [Pong](https://github.com/basilkohler/rusty_pong) * [TETRIS](https://github.com/madchicken/rust-tetris) * [Columns](https://github.com/Rendez/rust_columns) +* [Snake](https://github.com/konstcode/snake) ## Contribution