add screen abstraction
This commit is contained in:
parent
b0e9a1bc0b
commit
681c53eb80
7 changed files with 239 additions and 86 deletions
|
@ -4,6 +4,11 @@ version = "0.1.0"
|
|||
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||
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"
|
||||
serde_derive = "1.0"
|
||||
|
|
98
src/game.rs
98
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<Level>,
|
||||
current_level: usize,
|
||||
keymap: HashMap<VirtualKeyCode, bool>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
27
src/keymap.rs
Normal file
27
src/keymap.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use glium::glutin::VirtualKeyCode;
|
||||
|
||||
pub struct Keymap(HashMap<VirtualKeyCode, bool>);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
25
src/screens/menu.rs
Normal file
25
src/screens/menu.rs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
63
src/screens/mod.rs
Normal file
63
src/screens/mod.rs
Normal file
|
@ -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<dyn Screen>),
|
||||
}
|
||||
|
||||
pub struct ScreenStack(Vec<Box<dyn Screen>>);
|
||||
|
||||
impl ScreenStack {
|
||||
pub fn with<S: 'static + Screen>(screen: S) -> Self {
|
||||
let mut stack = Vec::<Box<Screen>>::new();
|
||||
stack.push(Box::new(screen));
|
||||
ScreenStack(stack)
|
||||
}
|
||||
|
||||
pub fn top(&self) -> impl AsRef<dyn Screen + 'static> + '_ {
|
||||
self.0.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn top_mut(&mut self) -> impl AsMut<dyn Screen + 'static> + '_ {
|
||||
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)
|
||||
}
|
||||
}
|
103
src/screens/play.rs
Normal file
103
src/screens/play.rs
Normal file
|
@ -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<Level>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue