This commit is contained in:
Michael Zhang 2019-08-06 03:16:06 -05:00
parent c0ce496ce0
commit e9c8cdbf48
No known key found for this signature in database
GPG key ID: 5BAEFE5D04F0CE6C
8 changed files with 340 additions and 37 deletions

View file

@ -11,7 +11,8 @@ const vec4 top = vec4(0.5, 0.5, 0.5, 1.0);
const vec4 bot = vec4(0.4, 0.4, 0.4, 1.0);
void main() {
outcolor = 0.2 * (bot * (1 - pos.y) + top * (1 - pos.x)) + 0.2;
outcolor = vec4(0.2 * (0.4 * (1 - pos.y) + 0.5 * (1 - pos.x)) + 0.2);
outcolor.w = 1.0;
// if ((pos.x > -threshold && pos.x < threshold)
// || (pos.y > -threshold && pos.y < threshold)
// || (pos.x > 1.0-threshold && pos.x < 1.0+threshold)

View file

@ -9,4 +9,4 @@ uniform mat4 projection;
void main() {
pos = vec4(point, 0.0, 1.0);
gl_Position = projection * target * pos;
}
}

View file

@ -1,6 +1,6 @@
#[derive(Debug, Deserialize)]
pub struct PlayerData {
pub position: [u32; 2],
pub position: [i32; 2],
pub color: [u32; 3],
}
@ -8,9 +8,9 @@ pub struct PlayerData {
pub struct BlockData {
pub movable: bool,
pub orientation: u32,
pub position: [u32; 2],
pub position: [i32; 2],
pub color: [u32; 3],
pub segments: Vec<[u32; 4]>,
pub segments: Vec<[i32; 4]>,
}
#[derive(Debug, Deserialize)]

View file

@ -1,11 +1,13 @@
#[derive(Copy, Clone)]
use std::ops::Add;
#[derive(Eq, PartialEq, Hash, PartialOrd, Copy, Clone)]
pub enum Board {
Left = 0,
Right = 1,
}
impl From<u32> for Board {
fn from(n: u32) -> Self {
impl From<i32> for Board {
fn from(n: i32) -> Self {
match n {
0 => Board::Left,
1 => Board::Right,
@ -40,7 +42,27 @@ pub enum PushDir {
Right,
}
#[derive(Copy, Clone)]
impl PushDir {
pub fn as_pair(&self) -> (i32, i32) {
match self {
PushDir::Up => (0, -1),
PushDir::Down => (0, 1),
PushDir::Left => (-1, 0),
PushDir::Right => (1, 0),
}
}
}
impl Add<PushDir> for (i32, i32, Board) {
type Output = (i32, i32, Board);
fn add(self, rhs: PushDir) -> Self::Output {
let offset = rhs.as_pair();
(self.0 + offset.0, self.1 + offset.1, self.2)
}
}
#[derive(Copy, Clone, PartialOrd, PartialEq)]
pub enum Shape {
Full = 0,
TopRight = 1,
@ -49,8 +71,8 @@ pub enum Shape {
BottomRight = 4,
}
impl From<u32> for Shape {
fn from(n: u32) -> Self {
impl From<i32> for Shape {
fn from(n: i32) -> Self {
match n {
0 => Shape::Full,
1 => Shape::TopRight,
@ -61,3 +83,16 @@ impl From<u32> for Shape {
}
}
}
impl Shape {
pub fn is_opposite(&self, other: &Shape) -> bool {
use Shape::*;
match (self, other) {
(TopRight, BottomLeft)
| (BottomLeft, TopRight)
| (TopLeft, BottomRight)
| (BottomRight, TopLeft) => true,
_ => false,
}
}
}

View file

@ -1,7 +1,10 @@
use std::collections::HashMap;
use std::time::Duration;
use glium::glutin::{ElementState, Event, VirtualKeyCode, WindowEvent};
use glium::{Display, Frame};
use crate::enums::PushDir;
use crate::level::Level;
use crate::renderer::Renderer;
use crate::resources::Resources;
@ -20,6 +23,7 @@ pub struct Game<'a> {
pub display: &'a Display,
levels: Vec<Level>,
current_level: usize,
keymap: HashMap<VirtualKeyCode, bool>,
}
impl<'a> Game<'a> {
@ -41,6 +45,26 @@ impl<'a> Game<'a> {
display,
levels,
current_level: 0,
keymap: HashMap::new(),
}
}
pub fn handle_event(&mut self, event: Event) {
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => self.resources.window_dimensions = size.into(),
WindowEvent::KeyboardInput { input, .. } => {
if let Some(code) = &input.virtual_keycode {
if let ElementState::Pressed = &input.state {
self.keymap.insert(*code, true);
} else {
self.keymap.insert(*code, false);
}
}
}
_ => (),
},
_ => (),
}
}
@ -52,7 +76,39 @@ impl<'a> Game<'a> {
self.levels.iter().nth(self.current_level).unwrap()
}
pub fn update(&mut self, delta: Duration) {}
pub fn get_current_level_mut(&mut self) -> &mut Level {
self.levels.iter_mut().nth(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, $player:expr, $movement:expr) => {
if self.is_pressed(&$key) {
let mut level = self.get_current_level_mut();
level.handle_movement($player, $movement);
self.keymap.insert($key, false);
}
};
}
shit!(VirtualKeyCode::W, true, PushDir::Up);
shit!(VirtualKeyCode::A, true, PushDir::Left);
shit!(VirtualKeyCode::S, true, PushDir::Down);
shit!(VirtualKeyCode::D, true, PushDir::Right);
shit!(VirtualKeyCode::I, false, PushDir::Up);
shit!(VirtualKeyCode::J, false, PushDir::Left);
shit!(VirtualKeyCode::K, false, PushDir::Down);
shit!(VirtualKeyCode::L, false, PushDir::Right);
}
pub fn render(&self, renderer: &mut Renderer) {
let level = self.get_current_level();

View file

@ -1,26 +1,35 @@
use std::collections::VecDeque;
use std::collections::{HashMap, VecDeque};
use crate::data::LevelData;
use crate::enums::{Board, Orientation, Shape};
use crate::enums::{Board, Orientation, PushDir, Shape};
use crate::renderer::Renderer;
use crate::{GAME_HEIGHT, GAME_WIDTH};
pub struct Level {
dimensions: (u32, u32),
move_stack: VecDeque<()>,
cell_map: CellMap,
blocks: Vec<Block>,
player1: Player,
player2: Player,
}
#[derive(Clone)]
pub struct Block {
position: (u32, u32),
movable: bool,
position: (i32, i32),
color: (f32, f32, f32),
orientation: Orientation,
segments: Vec<Segment>,
}
#[derive(Copy, Clone, PartialOrd, PartialEq)]
pub struct Segment(i32, i32, Shape, Board);
#[derive(Copy, Clone)]
pub struct Segment(u32, u32, Shape, Board);
pub struct Player {
pub position: [i32; 2],
pub color: [u32; 3],
}
impl Level {
pub fn from_json(data: impl AsRef<str>) -> Level {
@ -31,6 +40,7 @@ impl Level {
.blocks
.iter()
.map(|block| {
let movable = block.movable;
let position = (block.position[0], block.position[1]);
let segments = block
.segments
@ -46,6 +56,7 @@ impl Level {
block.color[2] as f32 / 256.0,
);
Block {
movable,
position,
color,
segments,
@ -54,40 +65,138 @@ impl Level {
})
.collect();
let player1 = Player {
position: data.player1.position,
color: data.player1.color,
};
let player2 = Player {
position: data.player2.position,
color: data.player2.color,
};
Level {
dimensions: (data.dimensions[0], data.dimensions[1]),
move_stack: VecDeque::new(),
cell_map: CellMap::new(),
blocks,
player1,
player2,
}
}
// player1: true -> player1, false -> player2
// TODO: don't use a boolean here
pub fn handle_movement(&mut self, player1: bool, direction: PushDir) -> bool {
let mut player = if player1 {
&self.player1
} else {
&self.player2
};
// TODO: check out of bounds
let movement = direction.as_pair();
let x = player.position[0] + movement.0;
let y = player.position[1] + movement.1;
let result = self.can_move(player1, direction).clone();
let mut player = if player1 {
&mut self.player1
} else {
&mut self.player2
};
if let Some(_) = result {
player.position[0] = x;
player.position[1] = y;
true
} else {
false
}
}
// TODO: don't use a boolean here
pub fn can_move(&self, player1: bool, direction: PushDir) -> Option<()> {
// an absolute segment (as opposed to relative to a block)
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct ASegment(i32, i32, Shape, Board);
fn can_push(src: Segment, dst: Segment) -> bool {
if src.3 != dst.3 {
return false;
}
true
}
let player = if player1 {
(
self.player1.position[0],
self.player1.position[1],
Board::Left,
)
} else {
(
self.player2.position[0],
self.player2.position[1],
Board::Right,
)
};
// check to make sure that the player isn't trying to go out of bounds
let target = player + direction;
if target.0 < 0
|| target.0 >= self.dimensions.0 as i32
|| target.1 < 0
|| target.1 >= self.dimensions.1 as i32
{
return None;
}
// check if we're sharing a triangle cell
if let CellContents::Double(a, b) = self.cell_map.get(player) {}
// 08/06 pickup
// need to determine whether or not segment should hold a reference back to block or not?
// either way, segment in the cellmap should hold block information
// maybe cellmap should just carry a block index? seems hacky
// using refs to manage the whole thing is messy and probably doesn't work
// ???
Some(())
}
pub fn render(&self, renderer: &mut Renderer) {
let playfield_ratio = (2 * self.dimensions.0 + 6) as f64 / (self.dimensions.1 + 4) as f64;
let screen_ratio = GAME_WIDTH as f64 / GAME_HEIGHT as f64;
// board positioning calculations
let playfield_ratio = (2 * self.dimensions.0 + 6) as f32 / (self.dimensions.1 + 4) as f32;
let screen_ratio = renderer.window.0 / renderer.window.1;
let cols = self.dimensions.0 as i32;
let rows = self.dimensions.1 as i32;
let (scale, xoff, yoff) = if playfield_ratio > screen_ratio {
let scale = GAME_WIDTH / (2 * self.dimensions.0 + 6);
let yoff = GAME_HEIGHT / 2 - (self.dimensions.1 + 4) * scale / 2;
let scale = renderer.window.0 as i32 / (2 * cols + 6);
let yoff = renderer.window.1 as i32 / 2 - (rows + 4) * scale / 2;
(scale, 0, yoff)
} else {
let scale = GAME_HEIGHT / (self.dimensions.1 + 4);
let xoff = GAME_WIDTH / 2 - (2 * self.dimensions.0 + 6) * scale / 2;
let scale = renderer.window.1 as i32 / (rows + 4);
let xoff = renderer.window.0 as i32 / 2 - (2 * cols + 6) * scale / 2;
(scale, xoff, 0)
};
self.render_boards(renderer, scale, (xoff, yoff));
}
fn render_boards(&self, renderer: &mut Renderer, scale: u32, offset: (u32, u32)) {
fn render_boards(&self, renderer: &mut Renderer, scale: i32, offset: (i32, i32)) {
let left_off = (offset.0 + 2 * scale, offset.1 + 2 * scale);
let right_off = (
offset.0 + (4 + self.dimensions.0) * scale,
offset.0 + (4 + self.dimensions.0 as i32) * scale,
offset.1 + 2 * scale,
);
// render the grid
for x in 0..self.dimensions.0 {
for y in 0..self.dimensions.1 {
// TODO: do this in one single pass instead of once for each cell
for x in 0..self.dimensions.0 as i32 {
for y in 0..self.dimensions.1 as i32 {
renderer.render_cell((left_off.0 + x * scale, left_off.1 + y * scale), scale);
renderer.render_cell((right_off.0 + x * scale, right_off.1 + y * scale), scale);
}
@ -105,5 +214,89 @@ impl Level {
renderer.render_segment(location, scale, block.color, block.orientation, segment.2);
}
}
// render player
self.render_player(renderer, &self.player1, scale, left_off);
self.render_player(renderer, &self.player2, scale, right_off);
}
fn render_player(
&self,
renderer: &mut Renderer,
player: &Player,
scale: i32,
offset: (i32, i32),
) {
let location = (
offset.0 + player.position[0] * scale + 4,
offset.1 + player.position[1] * scale + 4,
);
renderer.render_segment(
location,
(scale - 8),
(
player.color[0] as f32,
player.color[1] as f32,
player.color[2] as f32,
),
Orientation::Both,
Shape::Full,
);
}
}
struct CellMap(HashMap<(i32, i32, Board), CellContents>);
#[derive(Copy, Clone)]
enum CellContents {
Empty,
Player,
Single(Segment),
// invariant: .0 < .1
Double(Segment, Segment),
}
impl CellMap {
pub fn new() -> Self {
CellMap(HashMap::new())
}
pub fn get(&self, loc: (i32, i32, Board)) -> CellContents {
self.0
.get(&loc)
.cloned()
.unwrap_or_else(|| CellContents::Empty)
}
pub fn clear(&mut self, loc: (i32, i32, Board)) {
self.0.remove(&loc);
}
pub fn add(&mut self, loc: (i32, i32, Board), segment: &Segment) -> bool {
let contents = self.get(loc).clone();
match contents {
CellContents::Empty => {
// just add it like normal
self.0.insert(loc, CellContents::Single(*segment));
true
}
CellContents::Single(existing) => {
if existing.2.is_opposite(&segment.2) {
self.0.insert(
loc,
if *segment < existing {
CellContents::Double(*segment, existing)
} else {
CellContents::Double(existing, *segment)
},
);
true
} else {
false
}
}
CellContents::Player | CellContents::Double(_, _) => false,
}
}
}

View file

@ -41,7 +41,7 @@ fn main() {
println!("size: {:?}", window.get_inner_size());
}
let game = Game::new(&display);
let mut game = Game::new(&display);
let mut closed = false;
let mut prev = Instant::now();
@ -54,9 +54,11 @@ fn main() {
event: WindowEvent::CloseRequested,
..
} => closed = true,
_ => (),
_ => game.handle_event(event),
});
game.update(delta);
let mut target = display.draw();
target.clear(None, Some((0.0, 0.0, 0.0, 1.0)), true, None, None);
let mut renderer = game.create_renderer(&mut target);

View file

@ -5,9 +5,9 @@ use nalgebra::{Matrix4, Vector4};
use crate::enums::{Orientation, Shape};
use crate::game::Game;
use crate::{GAME_HEIGHT, GAME_WIDTH};
pub struct Renderer<'a, 'b> {
pub window: (f32, f32),
target: &'a mut Frame,
display: &'b Display,
cell_program: &'b Program,
@ -18,6 +18,10 @@ pub struct Renderer<'a, 'b> {
impl<'a, 'b> Renderer<'a, 'b> {
pub fn new(game: &'b Game, target: &'a mut Frame) -> Self {
Renderer {
window: (
game.resources.window_dimensions.0 as f32,
game.resources.window_dimensions.1 as f32,
),
target,
display: &game.display,
cell_program: game.resources.get_shader("cell").unwrap(),
@ -26,7 +30,7 @@ impl<'a, 'b> Renderer<'a, 'b> {
}
}
pub fn render_cell(&mut self, location: (u32, u32), scale: u32) {
pub fn render_cell(&mut self, location: (i32, i32), scale: i32) {
#[derive(Copy, Clone)]
struct Vertex {
point: [f32; 2],
@ -43,8 +47,14 @@ impl<'a, 'b> Renderer<'a, 'b> {
vertices.push(Vertex { point: [1.0, 0.0] });
let vertex_buffer = VertexBuffer::new(self.display, &vertices).unwrap();
let projection =
glm::ortho::<f32>(0.0, GAME_WIDTH as f32, GAME_HEIGHT as f32, 0.0, -1.0, 1.0);
let projection = glm::ortho::<f32>(
0.0,
self.window.0 as f32,
self.window.1 as f32,
0.0,
-1.0,
1.0,
);
let mut matrix = Matrix4::<f32>::identity();
matrix = matrix.append_nonuniform_scaling(&[scale as f32, scale as f32, 1.0].into());
matrix = matrix.append_translation(&[location.0 as f32, location.1 as f32, 0.0].into());
@ -73,8 +83,8 @@ impl<'a, 'b> Renderer<'a, 'b> {
pub fn render_segment(
&mut self,
location: (u32, u32),
scale: u32,
location: (i32, i32),
scale: i32,
color: (f32, f32, f32),
orientation: Orientation,
shape: Shape,
@ -175,8 +185,14 @@ impl<'a, 'b> Renderer<'a, 'b> {
let vertex_buffer = VertexBuffer::new(self.display, &vertices).unwrap();
let tint = Vector4::from([color.0, color.1, color.2, 1.0f32]);
let projection =
glm::ortho::<f32>(0.0, GAME_WIDTH as f32, GAME_HEIGHT as f32, 0.0, -1.0, 1.0);
let projection = glm::ortho::<f32>(
0.0,
self.window.0 as f32,
self.window.1 as f32,
0.0,
-1.0,
1.0,
);
let mut matrix = Matrix4::<f32>::identity();
matrix = matrix.append_nonuniform_scaling(&[scale as f32, scale as f32, 1.0].into());
matrix = matrix.append_translation(&[location.0 as f32, location.1 as f32, 0.0].into());