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>"]
|
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 'z'
|
||||||
|
lto = true
|
||||||
|
panic = 'abort'
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glium = "0.25"
|
glium = "0.25"
|
||||||
image = "0.21"
|
image = "0.21"
|
||||||
|
|
98
src/game.rs
98
src/game.rs
|
@ -6,9 +6,11 @@ use glium::{Display, Frame};
|
||||||
|
|
||||||
use crate::animations::AnimationState;
|
use crate::animations::AnimationState;
|
||||||
use crate::enums::{Board, PushDir};
|
use crate::enums::{Board, PushDir};
|
||||||
|
use crate::keymap::Keymap;
|
||||||
use crate::level::Level;
|
use crate::level::Level;
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::resources::Resources;
|
use crate::resources::Resources;
|
||||||
|
use crate::screens::{MenuScreen, Screen, ScreenStack};
|
||||||
|
|
||||||
const SEGMENT_VERT: &str = include_str!("../shaders/segment.vert");
|
const SEGMENT_VERT: &str = include_str!("../shaders/segment.vert");
|
||||||
const SEGMENT_FRAG: &str = include_str!("../shaders/segment.frag");
|
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 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 struct Game<'a> {
|
||||||
pub resources: Resources,
|
pub resources: Resources,
|
||||||
pub display: &'a Display,
|
pub display: &'a Display,
|
||||||
levels: Vec<Level>,
|
keymap: Keymap,
|
||||||
current_level: usize,
|
screen_stack: ScreenStack,
|
||||||
keymap: HashMap<VirtualKeyCode, bool>,
|
|
||||||
animations: AnimationState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Game<'a> {
|
impl<'a> Game<'a> {
|
||||||
|
@ -42,17 +39,14 @@ impl<'a> Game<'a> {
|
||||||
.load_shader(display, "cell", &CELL_VERT, &CELL_FRAG)
|
.load_shader(display, "cell", &CELL_VERT, &CELL_FRAG)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let levels = vec![
|
// bruh
|
||||||
Level::from_json(&LEVEL_TUTORIAL),
|
let screen_stack = ScreenStack::with(MenuScreen::new());
|
||||||
Level::from_json(&LEVEL_TUTORIAL2),
|
|
||||||
];
|
|
||||||
Game {
|
Game {
|
||||||
resources,
|
resources,
|
||||||
display,
|
display,
|
||||||
levels,
|
keymap: Keymap::new(),
|
||||||
current_level: 0,
|
screen_stack,
|
||||||
keymap: HashMap::new(),
|
|
||||||
animations: AnimationState::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +57,9 @@ impl<'a> Game<'a> {
|
||||||
WindowEvent::KeyboardInput { input, .. } => {
|
WindowEvent::KeyboardInput { input, .. } => {
|
||||||
if let Some(code) = &input.virtual_keycode {
|
if let Some(code) = &input.virtual_keycode {
|
||||||
if let ElementState::Pressed = &input.state {
|
if let ElementState::Pressed = &input.state {
|
||||||
self.keymap.insert(*code, true);
|
self.keymap.pressed(*code);
|
||||||
} else {
|
} else {
|
||||||
self.keymap.insert(*code, false);
|
self.keymap.release(*code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,77 +72,11 @@ impl<'a> Game<'a> {
|
||||||
Renderer::new(self, target)
|
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) {
|
pub fn update(&mut self, delta: Duration) {
|
||||||
macro_rules! shit {
|
self.screen_stack.update(delta, &self.keymap);
|
||||||
($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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, renderer: &mut Renderer) {
|
pub fn render(&self, renderer: &mut Renderer) {
|
||||||
let level = self.get_current_level();
|
self.screen_stack.render(renderer);
|
||||||
level.render(renderer, &self.animations);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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 data;
|
||||||
mod enums;
|
mod enums;
|
||||||
mod game;
|
mod game;
|
||||||
|
mod keymap;
|
||||||
mod level;
|
mod level;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod resources;
|
mod resources;
|
||||||
|
mod screens;
|
||||||
|
|
||||||
use std::time::Instant;
|
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