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
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..9462f73 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
@@ -33,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 52ed8a2..856c964 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,17 +13,17 @@ use std::{
};
use invaders::{
- frame::{self, new_frame, Drawable, Frame},
+ frame::{new_frame, Drawable, Reset, Frame},
invaders::Invaders,
level::Level,
menu::Menu,
- player::Player,
+ player::{Player, Player2Mode},
render,
score::Score,
};
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,12 @@ 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, player2_mode: &mut Player2Mode, to_reset: &mut Vec<&mut dyn Reset>) {
*in_menu = true;
- *player = Player::new();
- *invaders = Invaders::new();
+ *player2_mode = Player2Mode::Enabled(false);
+ for elem in to_reset {
+ elem.reset();
+ }
}
fn main() -> Result<(), Box> {
@@ -58,13 +60,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 = Player2Mode::Enabled(false);
'gameloop: loop {
// Per-frame init
@@ -82,6 +86,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 = Player2Mode::Enabled(true);
} else {
break 'gameloop;
}
@@ -107,11 +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");
- 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 player2_mode, &mut to_reset);
+ },
+ KeyCode::Char('e') => {
+ player2_mode = Player2Mode::Enabled(true);
+ },
_ => {}
}
}
@@ -119,17 +138,23 @@ fn main() -> Result<(), Box> {
// Updates
player.update(delta);
+ if player2_mode == Player2Mode::Enabled(true) { 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, &player2_mode];
+ if player2_mode == Player2Mode::Enabled(true) {
+ drawables.push(&player2);
+ }
for drawable in drawables {
drawable.draw(&mut curr_frame);
}
@@ -145,7 +170,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 player2_mode, &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..a88e268 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,40 @@ 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);
}
}
}
+#[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;
+ }
+ }
+ }
+}
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