Finish 2.3, Pushing boxes

This commit is contained in:
Timothy Warren 2020-07-23 16:35:54 -04:00
parent 8c37bacafb
commit 403be94983
2 changed files with 87 additions and 8 deletions

View File

@ -1,11 +1,11 @@
[package] [package]
name = "rust-sokoban" name = "rust-sokoban"
version = "0.1.0" version = "0.1.0"
authors = ["Timothy Warren <twarren@nabancard.com>"] authors = ["Timothy Warren <tim@timshomepage.net>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ggez = "0.5.1" ggez = "0.5.1"
specs = { version="0.15.0", features=["specs-derive"] } specs = { version="0.16.1", features=["specs-derive"] }

View File

@ -5,14 +5,19 @@ use ggez::graphics::DrawParam;
use ggez::graphics::Image; use ggez::graphics::Image;
use ggez::nalgebra as na; use ggez::nalgebra as na;
use ggez::{conf, event, Context, GameResult}; use ggez::{conf, event, Context, GameResult};
use specs::world::Index;
use specs::Entities;
use specs::NullStorage;
use specs::WriteStorage;
use specs::{ use specs::{
join::Join, Builder, Component, Read, ReadStorage, RunNow, System, VecStorage, World, WorldExt, join::Join, Builder, Component, ReadStorage, RunNow, System, VecStorage, World, WorldExt, Write,
Write, WriteStorage,
}; };
use std::collections::HashMap;
use std::path; use std::path;
const TILE_WIDTH: f32 = 32.0; const TILE_WIDTH: f32 = 32.0;
const MAP_WIDTH: u8 = 8;
const MAP_HEIGHT: u8 = 9;
// Components // Components
#[derive(Debug, Component, Clone, Copy)] #[derive(Debug, Component, Clone, Copy)]
@ -51,6 +56,14 @@ pub struct Box {}
#[storage(VecStorage)] #[storage(VecStorage)]
pub struct BoxSpot {} pub struct BoxSpot {}
#[derive(Component, Default)]
#[storage(NullStorage)]
pub struct Movable;
#[derive(Component, Default)]
#[storage(NullStorage)]
pub struct Immovable;
// Resources // Resources
#[derive(Default)] #[derive(Default)]
pub struct InputQueue { pub struct InputQueue {
@ -101,17 +114,77 @@ impl<'a> System<'a> for InputSystem {
// Data // Data
type SystemData = ( type SystemData = (
Write<'a, InputQueue>, Write<'a, InputQueue>,
Entities<'a>,
WriteStorage<'a, Position>, WriteStorage<'a, Position>,
ReadStorage<'a, Player>, ReadStorage<'a, Player>,
ReadStorage<'a, Movable>,
ReadStorage<'a, Immovable>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
let (mut input_queue, mut positions, players) = data; let (mut input_queue, entities, mut positions, players, movables, immovables) = data;
for (position, _player) in (&mut positions, &players).join() { let mut to_move = Vec::new();
for (position, _player) in (&positions, &players).join() {
// Get the first key pressed // Get the first key pressed
if let Some(key) = input_queue.keys_pressed.pop() { if let Some(key) = input_queue.keys_pressed.pop() {
// Apply the key to the position // get all the movables and immovables
let mov: HashMap<(u8, u8), Index> = (&entities, &movables, &positions)
.join()
.map(|t| ((t.2.x, t.2.y), t.0.id()))
.collect();
let immov: HashMap<(u8, u8), Index> = (&entities, &immovables, &positions)
.join()
.map(|t| ((t.2.x, t.2.y), t.0.id()))
.collect();
// Now iterate through current position to the end of the map
// on the correct axis and check what needs to move.
let (start, end, is_x) = match key {
KeyCode::Up => (position.y, 0, false),
KeyCode::Down => (position.y, MAP_HEIGHT, false),
KeyCode::Left => (position.x, 0, true),
KeyCode::Right => (position.x, MAP_WIDTH, true),
_ => continue,
};
let range: Vec<_> = if start < end {
(start..=end).collect()
} else {
(end..=start).rev().collect()
};
for x_or_y in range {
let pos = if is_x {
(x_or_y, position.y)
} else {
(position.x, x_or_y)
};
// find a movable
// if it exists, we try to move it and continue
// if it doesn't exist, we continue and try to find an immovable instead
match mov.get(&pos) {
Some(id) => to_move.push((key, id.clone())),
None => {
// find an immovable
// if it exists, we need to stop and not move anything
// if it doesn't exist, we stop because we found a gap
match immov.get(&pos) {
Some(_) => to_move.clear(),
None => break,
}
}
}
}
}
}
// Now actually move what needs to be moved
for (key, id) in to_move {
let position = positions.get_mut(entities.entity(id));
if let Some(position) = position {
match key { match key {
KeyCode::Up => position.y -= 1, KeyCode::Up => position.y -= 1,
KeyCode::Down => position.y += 1, KeyCode::Down => position.y += 1,
@ -171,6 +244,8 @@ pub fn register_components(world: &mut World) {
world.register::<Wall>(); world.register::<Wall>();
world.register::<Box>(); world.register::<Box>();
world.register::<BoxSpot>(); world.register::<BoxSpot>();
world.register::<Movable>();
world.register::<Immovable>();
} }
pub fn register_resources(world: &mut World) { pub fn register_resources(world: &mut World) {
@ -185,6 +260,7 @@ pub fn create_wall(world: &mut World, position: Position) {
path: "/images/wall.png".to_string(), path: "/images/wall.png".to_string(),
}) })
.with(Wall {}) .with(Wall {})
.with(Immovable)
.build(); .build();
} }
@ -205,6 +281,8 @@ pub fn create_box(world: &mut World, position: Position) {
.with(Renderable { .with(Renderable {
path: "/images/box.png".to_string(), path: "/images/box.png".to_string(),
}) })
.with(Box {})
.with(Movable)
.build(); .build();
} }
@ -227,6 +305,7 @@ pub fn create_player(world: &mut World, position: Position) {
path: "/images/player.png".to_string(), path: "/images/player.png".to_string(),
}) })
.with(Player {}) .with(Player {})
.with(Movable)
.build(); .build();
} }