From 681c53eb8000182adbb4516d9a412d4d2e754e2e Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Mon, 12 Aug 2019 21:17:26 -0500 Subject: [PATCH] add screen abstraction --- Cargo.toml | 7 ++- src/game.rs | 98 ++++++----------------------------------- src/keymap.rs | 27 ++++++++++++ src/main.rs | 2 + src/screens/menu.rs | 25 +++++++++++ src/screens/mod.rs | 63 +++++++++++++++++++++++++++ src/screens/play.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 239 insertions(+), 86 deletions(-) create mode 100644 src/keymap.rs create mode 100644 src/screens/menu.rs create mode 100644 src/screens/mod.rs create mode 100644 src/screens/play.rs diff --git a/Cargo.toml b/Cargo.toml index e366645..f1b5b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,11 @@ version = "0.1.0" authors = ["Michael Zhang "] edition = "2018" +[profile.release] +opt-level = 'z' +lto = true +panic = 'abort' + [dependencies] glium = "0.25" image = "0.21" @@ -11,4 +16,4 @@ json5 = "0.2" nalgebra = "0.18" nalgebra-glm = "0.4" serde = "1.0" -serde_derive = "1.0" \ No newline at end of file +serde_derive = "1.0" diff --git a/src/game.rs b/src/game.rs index 898cf37..f6a02c4 100644 --- a/src/game.rs +++ b/src/game.rs @@ -6,9 +6,11 @@ use glium::{Display, Frame}; use crate::animations::AnimationState; use crate::enums::{Board, PushDir}; +use crate::keymap::Keymap; use crate::level::Level; use crate::renderer::Renderer; use crate::resources::Resources; +use crate::screens::{MenuScreen, Screen, ScreenStack}; const SEGMENT_VERT: &str = include_str!("../shaders/segment.vert"); const SEGMENT_FRAG: &str = include_str!("../shaders/segment.frag"); @@ -17,16 +19,11 @@ const CELL_FRAG: &str = include_str!("../shaders/cell.frag"); const SEGMENT_IMAGE: &[u8] = include_bytes!("../textures/segment.png"); -const LEVEL_TUTORIAL: &str = include_str!("../levels/tutorial.json"); -const LEVEL_TUTORIAL2: &str = include_str!("../levels/tutorial2.json"); - pub struct Game<'a> { pub resources: Resources, pub display: &'a Display, - levels: Vec, - current_level: usize, - keymap: HashMap, - animations: AnimationState, + keymap: Keymap, + screen_stack: ScreenStack, } impl<'a> Game<'a> { @@ -42,17 +39,14 @@ impl<'a> Game<'a> { .load_shader(display, "cell", &CELL_VERT, &CELL_FRAG) .unwrap(); - let levels = vec![ - Level::from_json(&LEVEL_TUTORIAL), - Level::from_json(&LEVEL_TUTORIAL2), - ]; + // bruh + let screen_stack = ScreenStack::with(MenuScreen::new()); + Game { resources, display, - levels, - current_level: 0, - keymap: HashMap::new(), - animations: AnimationState::new(), + keymap: Keymap::new(), + screen_stack, } } @@ -63,9 +57,9 @@ impl<'a> Game<'a> { WindowEvent::KeyboardInput { input, .. } => { if let Some(code) = &input.virtual_keycode { if let ElementState::Pressed = &input.state { - self.keymap.insert(*code, true); + self.keymap.pressed(*code); } else { - self.keymap.insert(*code, false); + self.keymap.release(*code); } } } @@ -78,77 +72,11 @@ impl<'a> Game<'a> { Renderer::new(self, target) } - pub fn get_current_level(&self) -> &Level { - self.levels.get(self.current_level).unwrap() - } - - pub fn get_current_level_mut(&mut self) -> &mut Level { - self.levels.get_mut(self.current_level).unwrap() - } - - pub fn is_pressed(&self, code: VirtualKeyCode) -> bool { - if let Some(true) = self.keymap.get(&code) { - true - } else { - false - } - } - pub fn update(&mut self, delta: Duration) { - macro_rules! shit { - ($key:expr, $board:expr, $direction:expr) => { - if self.is_pressed($key) { - println!("pushed: {:?}", $key); - let level = self.get_current_level_mut(); - let result = level.try_move($board, $direction); - self.animations.begin_move_transition(result); - self.keymap.insert($key, false); - } - }; - } - - if self.animations.is_animating { - // println!("animating. {:?}", self.animations.progress); - self.animations.make_progress(delta); - - // we just finished! - if !self.animations.is_animating { - // apply the changes to the entities - // this indirection is used to dodge a concurrent borrow - let change_set = if let Some(Ok(change_set)) = &self.animations.last_move_result { - Some(change_set.clone()) - } else { - None - }; - if let Some(change_set) = change_set { - let level = self.get_current_level_mut(); - level.apply_change_set(change_set.clone()); - self.check_win_condition(); - } - } - } else { - shit!(VirtualKeyCode::W, Board::Left, PushDir::Up); - shit!(VirtualKeyCode::A, Board::Left, PushDir::Left); - shit!(VirtualKeyCode::S, Board::Left, PushDir::Down); - shit!(VirtualKeyCode::D, Board::Left, PushDir::Right); - - shit!(VirtualKeyCode::I, Board::Right, PushDir::Up); - shit!(VirtualKeyCode::J, Board::Right, PushDir::Left); - shit!(VirtualKeyCode::K, Board::Right, PushDir::Down); - shit!(VirtualKeyCode::L, Board::Right, PushDir::Right); - } - } - - fn check_win_condition(&mut self) { - let level = self.get_current_level(); - if level.check_win_condition() { - // go on to the next level - self.current_level += 1; - } + self.screen_stack.update(delta, &self.keymap); } pub fn render(&self, renderer: &mut Renderer) { - let level = self.get_current_level(); - level.render(renderer, &self.animations); + self.screen_stack.render(renderer); } } diff --git a/src/keymap.rs b/src/keymap.rs new file mode 100644 index 0000000..dfc959c --- /dev/null +++ b/src/keymap.rs @@ -0,0 +1,27 @@ +use std::collections::HashMap; + +use glium::glutin::VirtualKeyCode; + +pub struct Keymap(HashMap); + +impl Keymap { + pub fn new() -> Self { + Keymap(HashMap::new()) + } + + pub fn pressed(&mut self, code: VirtualKeyCode) { + self.0.insert(code, true); + } + + pub fn release(&mut self, code: VirtualKeyCode) { + self.0.insert(code, false); + } + + pub fn is_pressed(&self, code: VirtualKeyCode) -> bool { + if let Some(true) = self.0.get(&code) { + true + } else { + false + } + } +} diff --git a/src/main.rs b/src/main.rs index ed6b640..768f192 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,9 +9,11 @@ mod color; mod data; mod enums; mod game; +mod keymap; mod level; mod renderer; mod resources; +mod screens; use std::time::Instant; diff --git a/src/screens/menu.rs b/src/screens/menu.rs new file mode 100644 index 0000000..cc29329 --- /dev/null +++ b/src/screens/menu.rs @@ -0,0 +1,25 @@ +use std::time::Duration; + +use glium::glutin::VirtualKeyCode; + +use crate::keymap::Keymap; +use crate::screens::{PlayScreen, Screen, ScreenAction}; + +pub struct MenuScreen; + +impl Screen for MenuScreen { + fn update(&mut self, delta: Duration, keymap: &Keymap) -> ScreenAction { + if keymap.is_pressed(VirtualKeyCode::Space) { + let play_screen = PlayScreen::new(); + ScreenAction::Push(Box::new(play_screen)) + } else { + ScreenAction::None + } + } +} + +impl MenuScreen { + pub fn new() -> MenuScreen { + MenuScreen + } +} diff --git a/src/screens/mod.rs b/src/screens/mod.rs new file mode 100644 index 0000000..09efef7 --- /dev/null +++ b/src/screens/mod.rs @@ -0,0 +1,63 @@ +mod menu; +mod play; + +use std::sync::Arc; +use std::time::Duration; + +use crate::keymap::Keymap; +use crate::renderer::Renderer; + +pub use self::menu::MenuScreen; +pub use self::play::PlayScreen; + +pub trait Screen { + fn update(&mut self, delta: Duration, keymap: &Keymap) -> ScreenAction { + ScreenAction::None + } + + fn render(&self, renderer: &mut Renderer) {} +} + +pub enum ScreenAction { + None, + Push(Box), +} + +pub struct ScreenStack(Vec>); + +impl ScreenStack { + pub fn with(screen: S) -> Self { + let mut stack = Vec::>::new(); + stack.push(Box::new(screen)); + ScreenStack(stack) + } + + pub fn top(&self) -> impl AsRef + '_ { + self.0.last().unwrap() + } + + pub fn top_mut(&mut self) -> impl AsMut + '_ { + self.0.last_mut().unwrap() + } + + pub fn update(&mut self, delta: Duration, keymap: &Keymap) { + let result = { + let mut screen = self.top_mut(); + let screen = screen.as_mut(); + screen.update(delta, keymap) + }; + match result { + ScreenAction::None => (), + ScreenAction::Push(new_screen) => { + println!("pushed new screen"); + self.0.push(new_screen); + } + } + } + + pub fn render(&self, renderer: &mut Renderer) { + let mut screen = self.top(); + let screen = screen.as_ref(); + screen.render(renderer) + } +} diff --git a/src/screens/play.rs b/src/screens/play.rs new file mode 100644 index 0000000..c21195a --- /dev/null +++ b/src/screens/play.rs @@ -0,0 +1,103 @@ +use std::time::Duration; + +use glium::glutin::VirtualKeyCode; + +use crate::animations::AnimationState; +use crate::enums::{Board, PushDir}; +use crate::keymap::Keymap; +use crate::level::Level; +use crate::renderer::Renderer; +use crate::screens::{Screen, ScreenAction}; + +const LEVEL_TUTORIAL: &str = include_str!("../../levels/tutorial.json"); +const LEVEL_TUTORIAL2: &str = include_str!("../../levels/tutorial2.json"); + +pub struct PlayScreen { + animations: AnimationState, + levels: Vec, + current_level: usize, +} + +impl Screen for PlayScreen { + fn update(&mut self, delta: Duration, keymap: &Keymap) -> ScreenAction { + macro_rules! shit { + ($key:expr, $board:expr, $direction:expr) => { + if keymap.is_pressed($key) { + println!("pushed: {:?}", $key); + let level = self.get_current_level_mut(); + let result = level.try_move($board, $direction); + self.animations.begin_move_transition(result); + } + }; + } + + if self.animations.is_animating { + // println!("animating. {:?}", self.animations.progress); + self.animations.make_progress(delta); + + // we just finished! + if !self.animations.is_animating { + // apply the changes to the entities + // this indirection is used to dodge a concurrent borrow + let change_set = if let Some(Ok(change_set)) = &self.animations.last_move_result { + Some(change_set.clone()) + } else { + None + }; + if let Some(change_set) = change_set { + let level = self.get_current_level_mut(); + level.apply_change_set(change_set.clone()); + self.check_win_condition(); + } + } + } else { + shit!(VirtualKeyCode::W, Board::Left, PushDir::Up); + shit!(VirtualKeyCode::A, Board::Left, PushDir::Left); + shit!(VirtualKeyCode::S, Board::Left, PushDir::Down); + shit!(VirtualKeyCode::D, Board::Left, PushDir::Right); + + shit!(VirtualKeyCode::I, Board::Right, PushDir::Up); + shit!(VirtualKeyCode::J, Board::Right, PushDir::Left); + shit!(VirtualKeyCode::K, Board::Right, PushDir::Down); + shit!(VirtualKeyCode::L, Board::Right, PushDir::Right); + } + + ScreenAction::None + } + + fn render(&self, renderer: &mut Renderer) { + let level = self.get_current_level(); + level.render(renderer, &self.animations); + } +} + +impl PlayScreen { + pub fn get_current_level(&self) -> &Level { + self.levels.get(self.current_level).unwrap() + } + + pub fn get_current_level_mut(&mut self) -> &mut Level { + self.levels.get_mut(self.current_level).unwrap() + } + + pub fn new() -> PlayScreen { + let levels = vec![ + Level::from_json(&LEVEL_TUTORIAL), + Level::from_json(&LEVEL_TUTORIAL2), + ]; + + PlayScreen { + levels, + current_level: 0, + animations: AnimationState::new(), + } + } + + fn check_win_condition(&mut self) { + let level = self.get_current_level(); + if level.check_win_condition() { + // go on to the next level + self.current_level += 1; + } + } +}