huge refactor

This commit is contained in:
Michael Zhang 2019-08-07 02:44:49 -05:00
parent e9c8cdbf48
commit b0ef20ff8b
No known key found for this signature in database
GPG key ID: 5BAEFE5D04F0CE6C
11 changed files with 278 additions and 139 deletions

View file

@ -17,8 +17,8 @@
"position": [1, 3], "position": [1, 3],
"color": [255, 10, 100], "color": [255, 10, 100],
"segments": [ "segments": [
[0, 0, 0, 0], [1, 3, 0, 0],
[1, 0, 4, 0], [2, 3, 4, 0],
], ],
}, },
{ {
@ -27,8 +27,8 @@
"position": [2, 4], "position": [2, 4],
"color": [105, 210, 50], "color": [105, 210, 50],
"segments": [ "segments": [
[0, 0, 2, 0], [2, 4, 2, 0],
[0, 1, 0, 0], [2, 5, 0, 0],
], ],
}, },
{ {
@ -37,8 +37,8 @@
"position": [0, 4], "position": [0, 4],
"color": [35, 150, 100], "color": [35, 150, 100],
"segments": [ "segments": [
[0, 0, 1, 1], [0, 4, 1, 1],
[0, 1, 0, 1], [0, 5, 0, 1],
], ],
}, },
{ {
@ -47,8 +47,8 @@
"position": [0, 3], "position": [0, 3],
"color": [25, 120, 10], "color": [25, 120, 10],
"segments": [ "segments": [
[0, 0, 3, 1], [0, 3, 3, 1],
[1, 0, 0, 1], [1, 3, 0, 1],
], ],
}, },
{ {
@ -57,7 +57,7 @@
"position": [0, 2], "position": [0, 2],
"color": [15, 15, 15], "color": [15, 15, 15],
"segments": [ "segments": [
[0, 0, 0, 0], [0, 2, 0, 0],
], ],
}, },
{ {
@ -66,7 +66,7 @@
"position": [2, 2], "position": [2, 2],
"color": [15, 15, 15], "color": [15, 15, 15],
"segments": [ "segments": [
[0, 0, 0, 1], [2, 2, 0, 1],
], ],
}, },
] ]

14
src/color.rs Normal file
View file

@ -0,0 +1,14 @@
#[derive(Copy, Clone, Debug)]
pub struct Color(pub f32, pub f32, pub f32, pub f32);
impl Color {
pub fn from_rgb_u32(r: u32, g: u32, b: u32) -> Self {
Color(r as f32 / 256.0, g as f32 / 256.0, b as f32 / 256.0, 1.0)
}
}
impl From<(u32, u32, u32)> for Color {
fn from(tuple: (u32, u32, u32)) -> Self {
Color::from_rgb_u32(tuple.0, tuple.1, tuple.2)
}
}

View file

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

View file

@ -1,6 +1,6 @@
use std::ops::Add; use std::ops::Add;
#[derive(Eq, PartialEq, Hash, PartialOrd, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Hash, PartialOrd, Copy, Clone)]
pub enum Board { pub enum Board {
Left = 0, Left = 0,
Right = 1, Right = 1,
@ -18,9 +18,10 @@ impl From<i32> for Board {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum Orientation { pub enum Orientation {
Both = 0, None = 0,
Horizontal = 1, Horizontal = 1,
Vertical = 2, Vertical = 2,
Both = 3,
} }
impl From<u32> for Orientation { impl From<u32> for Orientation {
@ -62,7 +63,7 @@ impl Add<PushDir> for (i32, i32, Board) {
} }
} }
#[derive(Copy, Clone, PartialOrd, PartialEq)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub enum Shape { pub enum Shape {
Full = 0, Full = 0,
TopRight = 1, TopRight = 1,
@ -85,6 +86,17 @@ impl From<i32> for Shape {
} }
impl Shape { impl Shape {
pub fn opposite(&self) -> Shape {
use Shape::*;
match self {
TopRight => BottomLeft,
BottomLeft => TopRight,
TopLeft => BottomRight,
BottomRight => TopLeft,
Full => Full,
}
}
pub fn is_opposite(&self, other: &Shape) -> bool { pub fn is_opposite(&self, other: &Shape) -> bool {
use Shape::*; use Shape::*;
match (self, other) { match (self, other) {

View file

@ -92,7 +92,7 @@ impl<'a> Game<'a> {
macro_rules! shit { macro_rules! shit {
($key:expr, $player:expr, $movement:expr) => { ($key:expr, $player:expr, $movement:expr) => {
if self.is_pressed(&$key) { if self.is_pressed(&$key) {
let mut level = self.get_current_level_mut(); let level = self.get_current_level_mut();
level.handle_movement($player, $movement); level.handle_movement($player, $movement);
self.keymap.insert($key, false); self.keymap.insert($key, false);
} }

63
src/level/block.rs Normal file
View file

@ -0,0 +1,63 @@
use crate::color::Color;
use crate::data::BlockData;
use crate::enums::Orientation;
use crate::level::Segment;
pub trait Blockish {
fn get_color(&self) -> Color;
fn get_orientation(&self) -> Orientation;
// TODO: don't alloc/clone here?
fn get_segments(&self) -> Vec<Segment>;
}
#[derive(Clone)]
pub struct Block {
movable: bool,
position: (i32, i32),
color: Color,
orientation: Orientation,
segments: Vec<Segment>,
}
impl Block {
pub fn from_data(index: usize, data: &BlockData) -> Self {
let movable = data.movable;
let position = (data.position.0, data.position.1);
let segments = data
.segments
.iter()
.map(|segment| {
let seg = Segment {
position: (segment[0], segment[1]),
shape: segment[2].into(),
board: segment[3].into(),
};
seg
})
.collect();
let orientation = data.orientation.into();
let color = Color::from_rgb_u32(data.color.0, data.color.1, data.color.2);
Block {
movable,
position,
color,
segments,
orientation,
}
}
}
impl Blockish for Block {
fn get_color(&self) -> Color {
self.color
}
fn get_orientation(&self) -> Orientation {
self.orientation
}
fn get_segments(&self) -> Vec<Segment> {
self.segments.clone()
}
}

72
src/level/cell_map.rs Normal file
View file

@ -0,0 +1,72 @@
use std::collections::HashMap;
use crate::enums::Board;
use crate::level::Segment;
#[derive(Debug)]
pub struct CellMap(HashMap<(i32, i32, Board), CellContents>);
#[derive(Copy, Clone, Debug)]
pub enum CellContents {
Empty,
Player,
Single((usize, Segment)),
// invariant: .0 < .1
Double((usize, Segment), (usize, 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_player(&mut self, loc: (i32, i32, Board)) -> bool {
let contents = self.get(loc).clone();
match contents {
CellContents::Empty => {
self.0.insert(loc, CellContents::Player);
true
}
_ => false,
}
}
pub fn add(&mut self, loc: (i32, i32, Board), index: usize, segment: &Segment) -> bool {
let contents = self.get(loc).clone();
match contents {
CellContents::Empty => {
// just add it like normal
self.0.insert(loc, CellContents::Single((index, *segment)));
true
}
CellContents::Single((index0, existing)) => {
if existing.shape.is_opposite(&segment.shape) {
self.0.insert(
loc,
if *segment < existing {
CellContents::Double((index, *segment), (index0, existing))
} else {
CellContents::Double((index0, existing), (index, *segment))
},
);
true
} else {
false
}
}
CellContents::Player | CellContents::Double(_, _) => false,
}
}
}

View file

@ -1,9 +1,18 @@
mod block;
mod cell_map;
mod player;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use crate::color::Color;
use crate::data::LevelData; use crate::data::LevelData;
use crate::enums::{Board, Orientation, PushDir, Shape}; use crate::enums::{Board, Orientation, PushDir, Shape};
use crate::renderer::Renderer; use crate::renderer::Renderer;
use self::block::{Block, Blockish};
use self::cell_map::{CellContents, CellMap};
use self::player::Player;
pub struct Level { pub struct Level {
dimensions: (u32, u32), dimensions: (u32, u32),
move_stack: VecDeque<()>, move_stack: VecDeque<()>,
@ -13,22 +22,11 @@ pub struct Level {
player2: Player, player2: Player,
} }
#[derive(Clone)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub struct Block { pub struct Segment {
movable: bool,
position: (i32, i32), position: (i32, i32),
color: (f32, f32, f32), shape: Shape,
orientation: Orientation, board: Board,
segments: Vec<Segment>,
}
#[derive(Copy, Clone, PartialOrd, PartialEq)]
pub struct Segment(i32, i32, Shape, Board);
#[derive(Copy, Clone)]
pub struct Player {
pub position: [i32; 2],
pub color: [u32; 3],
} }
impl Level { impl Level {
@ -36,48 +34,48 @@ impl Level {
let data: LevelData = json5::from_str(data.as_ref()).unwrap(); let data: LevelData = json5::from_str(data.as_ref()).unwrap();
println!("{:?}", data); println!("{:?}", data);
let mut cell_map = CellMap::new();
let blocks = data let blocks = data
.blocks .blocks
.iter() .iter()
.map(|block| { .enumerate()
let movable = block.movable; .map(|(i, block)| {
let position = (block.position[0], block.position[1]); let block = Block::from_data(i, block);
let segments = block for segment in block.get_segments() {
.segments cell_map.add(
.iter() (segment.position.0, segment.position.1, segment.board),
.map(|segment| { i,
Segment(segment[0], segment[1], segment[2].into(), segment[3].into()) &segment,
})
.collect();
let orientation = block.orientation.into();
let color = (
block.color[0] as f32 / 256.0,
block.color[1] as f32 / 256.0,
block.color[2] as f32 / 256.0,
); );
Block {
movable,
position,
color,
segments,
orientation,
} }
block
}) })
.collect(); .collect();
let player1 = Player { let player1 = Player {
position: data.player1.position, position: data.player1.position,
color: data.player1.color, color: data.player1.color.into(),
}; };
let player2 = Player { let player2 = Player {
position: data.player2.position, position: data.player2.position,
color: data.player2.color, color: data.player2.color.into(),
}; };
cell_map.add_player((
data.player1.position.0,
data.player1.position.1,
Board::Left,
));
cell_map.add_player((
data.player2.position.0,
data.player2.position.1,
Board::Right,
));
Level { Level {
dimensions: (data.dimensions[0], data.dimensions[1]), dimensions: (data.dimensions[0], data.dimensions[1]),
move_stack: VecDeque::new(), move_stack: VecDeque::new(),
cell_map: CellMap::new(), cell_map,
blocks, blocks,
player1, player1,
player2, player2,
@ -87,7 +85,7 @@ impl Level {
// player1: true -> player1, false -> player2 // player1: true -> player1, false -> player2
// TODO: don't use a boolean here // TODO: don't use a boolean here
pub fn handle_movement(&mut self, player1: bool, direction: PushDir) -> bool { pub fn handle_movement(&mut self, player1: bool, direction: PushDir) -> bool {
let mut player = if player1 { let player = if player1 {
&self.player1 &self.player1
} else { } else {
&self.player2 &self.player2
@ -95,8 +93,8 @@ impl Level {
// TODO: check out of bounds // TODO: check out of bounds
let movement = direction.as_pair(); let movement = direction.as_pair();
let x = player.position[0] + movement.0; let x = player.position.0 + movement.0;
let y = player.position[1] + movement.1; let y = player.position.1 + movement.1;
let result = self.can_move(player1, direction).clone(); let result = self.can_move(player1, direction).clone();
let mut player = if player1 { let mut player = if player1 {
@ -106,21 +104,25 @@ impl Level {
}; };
if let Some(_) = result { if let Some(_) = result {
player.position[0] = x; player.position.0 = x;
player.position[1] = y; player.position.1 = y;
true true
} else { } else {
false false
} }
} }
pub fn try_move(&self) {}
// TODO: don't use a boolean here // TODO: don't use a boolean here
pub fn can_move(&self, player1: bool, direction: PushDir) -> Option<()> { pub fn can_move(&self, player1: bool, direction: PushDir) -> Option<()> {
// an absolute segment (as opposed to relative to a block) // an absolute segment (as opposed to relative to a block)
#[derive(Copy, Clone, PartialOrd, PartialEq)] #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct ASegment(i32, i32, Shape, Board); struct ASegment(i32, i32, Shape, Board);
fn can_push(src: Segment, dst: Segment) -> bool { struct PushMap(CellMap);
fn can_push_segment(src: ASegment, dst: ASegment) -> bool {
if src.3 != dst.3 { if src.3 != dst.3 {
return false; return false;
} }
@ -130,14 +132,14 @@ impl Level {
let player = if player1 { let player = if player1 {
( (
self.player1.position[0], self.player1.position.0,
self.player1.position[1], self.player1.position.1,
Board::Left, Board::Left,
) )
} else { } else {
( (
self.player2.position[0], self.player2.position.0,
self.player2.position[1], self.player2.position.1,
Board::Right, Board::Right,
) )
}; };
@ -153,7 +155,9 @@ impl Level {
} }
// check if we're sharing a triangle cell // check if we're sharing a triangle cell
if let CellContents::Double(a, b) = self.cell_map.get(player) {} if let CellContents::Double(a, b) = self.cell_map.get(player) {
// get the shape of the other block
}
// 08/06 pickup // 08/06 pickup
// need to determine whether or not segment should hold a reference back to block or not? // need to determine whether or not segment should hold a reference back to block or not?
@ -204,14 +208,22 @@ impl Level {
// render blocks // render blocks
for block in self.blocks.iter() { for block in self.blocks.iter() {
for segment in block.segments.iter() { for segment in block.get_segments().iter() {
let offset = match &segment.3 { let offset = match &segment.board {
Board::Left => left_off, Board::Left => left_off,
Board::Right => right_off, Board::Right => right_off,
}; };
let coord = (segment.0 + block.position.0, segment.1 + block.position.1); let location = (
let location = (offset.0 + coord.0 * scale, offset.1 + coord.1 * scale); offset.0 + segment.position.0 * scale,
renderer.render_segment(location, scale, block.color, block.orientation, segment.2); offset.1 + segment.position.1 * scale,
);
renderer.render_segment(
location,
scale,
block.get_color(),
block.get_orientation(),
segment.shape,
);
} }
} }
@ -228,75 +240,15 @@ impl Level {
offset: (i32, i32), offset: (i32, i32),
) { ) {
let location = ( let location = (
offset.0 + player.position[0] * scale + 4, offset.0 + player.position.0 * scale + 4,
offset.1 + player.position[1] * scale + 4, offset.1 + player.position.1 * scale + 4,
); );
renderer.render_segment( renderer.render_segment(
location, location,
(scale - 8), (scale - 8),
( player.color,
player.color[0] as f32,
player.color[1] as f32,
player.color[2] as f32,
),
Orientation::Both, Orientation::Both,
Shape::Full, 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,
}
}
}

23
src/level/player.rs Normal file
View file

@ -0,0 +1,23 @@
use crate::color::Color;
use crate::enums::Orientation;
use crate::level::{Blockish, Segment};
#[derive(Copy, Clone)]
pub struct Player {
pub position: (i32, i32),
pub color: Color,
}
impl Blockish for Player {
fn get_color(&self) -> Color {
self.color
}
fn get_orientation(&self) -> Orientation {
Orientation::None
}
fn get_segments(&self) -> Vec<Segment> {
vec![]
}
}

View file

@ -4,6 +4,7 @@ extern crate nalgebra_glm as glm;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
mod color;
mod data; mod data;
mod enums; mod enums;
mod game; mod game;

View file

@ -3,6 +3,7 @@ use glium::index::{NoIndices, PrimitiveType};
use glium::{Display, Frame, Program, Surface, Texture2d, VertexBuffer}; use glium::{Display, Frame, Program, Surface, Texture2d, VertexBuffer};
use nalgebra::{Matrix4, Vector4}; use nalgebra::{Matrix4, Vector4};
use crate::color::Color;
use crate::enums::{Orientation, Shape}; use crate::enums::{Orientation, Shape};
use crate::game::Game; use crate::game::Game;
@ -85,7 +86,7 @@ impl<'a, 'b> Renderer<'a, 'b> {
&mut self, &mut self,
location: (i32, i32), location: (i32, i32),
scale: i32, scale: i32,
color: (f32, f32, f32), color: Color,
orientation: Orientation, orientation: Orientation,
shape: Shape, shape: Shape,
) { ) {
@ -199,6 +200,7 @@ impl<'a, 'b> Renderer<'a, 'b> {
let rotate_texture = match orientation { let rotate_texture = match orientation {
Orientation::Both => false, Orientation::Both => false,
Orientation::None => false,
Orientation::Vertical => true, Orientation::Vertical => true,
Orientation::Horizontal => false, Orientation::Horizontal => false,
}; };