forked from tutorials/rust-sokoban
Completed 2.4 Modules
This commit is contained in:
parent
5c99a24d4d
commit
f7586c81e2
57
src/components.rs
Normal file
57
src/components.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use specs::{Component, NullStorage, VecStorage, World, WorldExt};
|
||||||
|
|
||||||
|
// Components
|
||||||
|
#[derive(Debug, Component, Clone, Copy)]
|
||||||
|
#[storage(VecStorage)]
|
||||||
|
pub struct Position {
|
||||||
|
pub x: u8,
|
||||||
|
pub y: u8,
|
||||||
|
pub z: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn new(x: u8, y: u8) -> Self {
|
||||||
|
Position { x, y, z: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[storage(VecStorage)]
|
||||||
|
pub struct Renderable {
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[storage(VecStorage)]
|
||||||
|
pub struct Wall {}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[storage(VecStorage)]
|
||||||
|
pub struct Player {}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[storage(VecStorage)]
|
||||||
|
pub struct Box {}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
#[storage(VecStorage)]
|
||||||
|
pub struct BoxSpot {}
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
#[storage(NullStorage)]
|
||||||
|
pub struct Movable;
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
#[storage(NullStorage)]
|
||||||
|
pub struct Immovable;
|
||||||
|
|
||||||
|
pub fn register_components(world: &mut World) {
|
||||||
|
world.register::<Position>();
|
||||||
|
world.register::<Renderable>();
|
||||||
|
world.register::<Player>();
|
||||||
|
world.register::<Wall>();
|
||||||
|
world.register::<Box>();
|
||||||
|
world.register::<BoxSpot>();
|
||||||
|
world.register::<Movable>();
|
||||||
|
world.register::<Immovable>();
|
||||||
|
}
|
3
src/constants.rs
Normal file
3
src/constants.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub const TILE_WIDTH: f32 = 32.0;
|
||||||
|
pub const MAP_WIDTH: u8 = 8;
|
||||||
|
pub const MAP_HEIGHT: u8 = 9;
|
59
src/entities.rs
Normal file
59
src/entities.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use crate::components::*;
|
||||||
|
use specs::{Builder, World, WorldExt};
|
||||||
|
|
||||||
|
pub fn create_wall(world: &mut World, position: Position) {
|
||||||
|
world
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { z: 10, ..position })
|
||||||
|
.with(Renderable {
|
||||||
|
path: "/images/wall.png".to_string(),
|
||||||
|
})
|
||||||
|
.with(Wall {})
|
||||||
|
.with(Immovable)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_floor(world: &mut World, position: Position) {
|
||||||
|
world
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { z: 5, ..position })
|
||||||
|
.with(Renderable {
|
||||||
|
path: "/images/floor.png".to_string(),
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_box(world: &mut World, position: Position) {
|
||||||
|
world
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { z: 10, ..position })
|
||||||
|
.with(Renderable {
|
||||||
|
path: "/images/box.png".to_string(),
|
||||||
|
})
|
||||||
|
.with(Box {})
|
||||||
|
.with(Movable)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_box_spot(world: &mut World, position: Position) {
|
||||||
|
world
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { z: 9, ..position })
|
||||||
|
.with(Renderable {
|
||||||
|
path: "/images/box_spot.png".to_string(),
|
||||||
|
})
|
||||||
|
.with(BoxSpot {})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_player(world: &mut World, position: Position) {
|
||||||
|
world
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { z: 10, ..position })
|
||||||
|
.with(Renderable {
|
||||||
|
path: "/images/player.png".to_string(),
|
||||||
|
})
|
||||||
|
.with(Player {})
|
||||||
|
.with(Movable)
|
||||||
|
.build();
|
||||||
|
}
|
317
src/main.rs
317
src/main.rs
@ -1,201 +1,21 @@
|
|||||||
use ggez;
|
use ggez;
|
||||||
use ggez::event::{KeyCode, KeyMods};
|
use ggez::event::KeyCode;
|
||||||
use ggez::graphics;
|
use ggez::event::KeyMods;
|
||||||
use ggez::graphics::DrawParam;
|
|
||||||
use ggez::graphics::Image;
|
|
||||||
use ggez::nalgebra as na;
|
|
||||||
use ggez::{conf, event, Context, GameResult};
|
use ggez::{conf, event, Context, GameResult};
|
||||||
use specs::world::Index;
|
use specs::{RunNow, World, WorldExt};
|
||||||
use specs::Entities;
|
|
||||||
use specs::NullStorage;
|
|
||||||
use specs::WriteStorage;
|
|
||||||
use specs::{
|
|
||||||
join::Join, Builder, Component, ReadStorage, RunNow, System, VecStorage, World, WorldExt, Write,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
const TILE_WIDTH: f32 = 32.0;
|
mod components;
|
||||||
const MAP_WIDTH: u8 = 8;
|
mod constants;
|
||||||
const MAP_HEIGHT: u8 = 9;
|
mod entities;
|
||||||
|
mod map;
|
||||||
|
mod resources;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
// Components
|
use crate::components::*;
|
||||||
#[derive(Debug, Component, Clone, Copy)]
|
use crate::map::*;
|
||||||
#[storage(VecStorage)]
|
use crate::resources::*;
|
||||||
pub struct Position {
|
use crate::systems::*;
|
||||||
x: u8,
|
|
||||||
y: u8,
|
|
||||||
z: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Position {
|
|
||||||
pub fn new(x: u8, y: u8) -> Self {
|
|
||||||
Position { x, y, z: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
#[storage(VecStorage)]
|
|
||||||
pub struct Renderable {
|
|
||||||
path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
#[storage(VecStorage)]
|
|
||||||
pub struct Wall {}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
#[storage(VecStorage)]
|
|
||||||
pub struct Player {}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
#[storage(VecStorage)]
|
|
||||||
pub struct Box {}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
#[storage(VecStorage)]
|
|
||||||
pub struct BoxSpot {}
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
#[storage(NullStorage)]
|
|
||||||
pub struct Movable;
|
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
|
||||||
#[storage(NullStorage)]
|
|
||||||
pub struct Immovable;
|
|
||||||
|
|
||||||
// Resources
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InputQueue {
|
|
||||||
pub keys_pressed: Vec<KeyCode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Systems
|
|
||||||
pub struct RenderingSystem<'a> {
|
|
||||||
context: &'a mut Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> System<'a> for RenderingSystem<'a> {
|
|
||||||
type SystemData = (ReadStorage<'a, Position>, ReadStorage<'a, Renderable>);
|
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
|
||||||
let (positions, renderables) = data;
|
|
||||||
|
|
||||||
// Clear the screen/set the background
|
|
||||||
graphics::clear(self.context, graphics::Color::new(0.95, 0.95, 0.95, 1.0));
|
|
||||||
|
|
||||||
// Get all the renderables with their positions and sort by the position z
|
|
||||||
// This will allow us to have entities layered visually.
|
|
||||||
let mut rendering_data = (&positions, &renderables).join().collect::<Vec<_>>();
|
|
||||||
rendering_data.sort_by(|&a, &b| a.0.z.partial_cmp(&b.0.z).expect("expected comparison"));
|
|
||||||
|
|
||||||
// Iterate through all paris of positions & renderables, load the image
|
|
||||||
// and draw it at the specified position.
|
|
||||||
for (position, renderable) in rendering_data.iter() {
|
|
||||||
// Load the image
|
|
||||||
let image = Image::new(self.context, renderable.path.clone()).expect("expected image");
|
|
||||||
let x = position.x as f32 * TILE_WIDTH;
|
|
||||||
let y = position.y as f32 * TILE_WIDTH;
|
|
||||||
|
|
||||||
// draw
|
|
||||||
let draw_params = DrawParam::new().dest(na::Point2::new(x, y));
|
|
||||||
graphics::draw(self.context, &image, draw_params).expect("expected render");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, present the context, this will actually display everything
|
|
||||||
// on the screen.
|
|
||||||
graphics::present(self.context).expect("expected to present");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InputSystem {}
|
|
||||||
|
|
||||||
impl<'a> System<'a> for InputSystem {
|
|
||||||
// Data
|
|
||||||
type SystemData = (
|
|
||||||
Write<'a, InputQueue>,
|
|
||||||
Entities<'a>,
|
|
||||||
WriteStorage<'a, Position>,
|
|
||||||
ReadStorage<'a, Player>,
|
|
||||||
ReadStorage<'a, Movable>,
|
|
||||||
ReadStorage<'a, Immovable>,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
|
||||||
let (mut input_queue, entities, mut positions, players, movables, immovables) = data;
|
|
||||||
|
|
||||||
let mut to_move = Vec::new();
|
|
||||||
|
|
||||||
for (position, _player) in (&positions, &players).join() {
|
|
||||||
// Get the first key pressed
|
|
||||||
if let Some(key) = input_queue.keys_pressed.pop() {
|
|
||||||
// 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 {
|
|
||||||
KeyCode::Up => position.y -= 1,
|
|
||||||
KeyCode::Down => position.y += 1,
|
|
||||||
KeyCode::Left => position.x -= 1,
|
|
||||||
KeyCode::Right => position.x += 1,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the game state
|
// All the game state
|
||||||
struct Game {
|
struct Game {
|
||||||
@ -230,122 +50,11 @@ impl event::EventHandler for Game {
|
|||||||
_keymod: KeyMods,
|
_keymod: KeyMods,
|
||||||
_repeat: bool,
|
_repeat: bool,
|
||||||
) {
|
) {
|
||||||
println!("Key pressed: {:?}", keycode);
|
|
||||||
|
|
||||||
let mut input_queue = self.world.write_resource::<InputQueue>();
|
let mut input_queue = self.world.write_resource::<InputQueue>();
|
||||||
input_queue.keys_pressed.push(keycode);
|
input_queue.keys_pressed.push(keycode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_components(world: &mut World) {
|
|
||||||
world.register::<Position>();
|
|
||||||
world.register::<Renderable>();
|
|
||||||
world.register::<Player>();
|
|
||||||
world.register::<Wall>();
|
|
||||||
world.register::<Box>();
|
|
||||||
world.register::<BoxSpot>();
|
|
||||||
world.register::<Movable>();
|
|
||||||
world.register::<Immovable>();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_resources(world: &mut World) {
|
|
||||||
world.insert(InputQueue::default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_wall(world: &mut World, position: Position) {
|
|
||||||
world
|
|
||||||
.create_entity()
|
|
||||||
.with(Position { z: 10, ..position })
|
|
||||||
.with(Renderable {
|
|
||||||
path: "/images/wall.png".to_string(),
|
|
||||||
})
|
|
||||||
.with(Wall {})
|
|
||||||
.with(Immovable)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_floor(world: &mut World, position: Position) {
|
|
||||||
world
|
|
||||||
.create_entity()
|
|
||||||
.with(Position { z: 5, ..position })
|
|
||||||
.with(Renderable {
|
|
||||||
path: "/images/floor.png".to_string(),
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_box(world: &mut World, position: Position) {
|
|
||||||
world
|
|
||||||
.create_entity()
|
|
||||||
.with(Position { z: 10, ..position })
|
|
||||||
.with(Renderable {
|
|
||||||
path: "/images/box.png".to_string(),
|
|
||||||
})
|
|
||||||
.with(Box {})
|
|
||||||
.with(Movable)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_box_spot(world: &mut World, position: Position) {
|
|
||||||
world
|
|
||||||
.create_entity()
|
|
||||||
.with(Position { z: 9, ..position })
|
|
||||||
.with(Renderable {
|
|
||||||
path: "/images/box_spot.png".to_string(),
|
|
||||||
})
|
|
||||||
.with(BoxSpot {})
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_player(world: &mut World, position: Position) {
|
|
||||||
world
|
|
||||||
.create_entity()
|
|
||||||
.with(Position { z: 10, ..position })
|
|
||||||
.with(Renderable {
|
|
||||||
path: "/images/player.png".to_string(),
|
|
||||||
})
|
|
||||||
.with(Player {})
|
|
||||||
.with(Movable)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_map(world: &mut World, map_string: String) {
|
|
||||||
// read all lines
|
|
||||||
let rows: Vec<&str> = map_string.trim().split('\n').map(|x| x.trim()).collect();
|
|
||||||
|
|
||||||
for (y, row) in rows.iter().enumerate() {
|
|
||||||
let columns: Vec<&str> = row.split(' ').collect();
|
|
||||||
|
|
||||||
for (x, column) in columns.iter().enumerate() {
|
|
||||||
// Create the position at which to create something on the map
|
|
||||||
let position = Position::new(x as u8, y as u8);
|
|
||||||
|
|
||||||
// Figure out which object to create
|
|
||||||
match *column {
|
|
||||||
"." => create_floor(world, position),
|
|
||||||
"W" => {
|
|
||||||
create_floor(world, position);
|
|
||||||
create_wall(world, position);
|
|
||||||
}
|
|
||||||
"P" => {
|
|
||||||
create_floor(world, position);
|
|
||||||
create_player(world, position);
|
|
||||||
}
|
|
||||||
"B" => {
|
|
||||||
create_floor(world, position);
|
|
||||||
create_box(world, position);
|
|
||||||
}
|
|
||||||
"S" => {
|
|
||||||
create_floor(world, position);
|
|
||||||
create_box_spot(world, position);
|
|
||||||
}
|
|
||||||
"N" => (),
|
|
||||||
c => panic!("unrecognized map item {}", c),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize_level(world: &mut World) {
|
pub fn initialize_level(world: &mut World) {
|
||||||
const MAP: &str = "
|
const MAP: &str = "
|
||||||
N N W W W W W W
|
N N W W W W W W
|
||||||
|
40
src/map.rs
Normal file
40
src/map.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use crate::components::Position;
|
||||||
|
use crate::entities::*;
|
||||||
|
use specs::World;
|
||||||
|
|
||||||
|
pub fn load_map(world: &mut World, map_string: String) {
|
||||||
|
// read all lines
|
||||||
|
let rows: Vec<&str> = map_string.trim().split('\n').map(|x| x.trim()).collect();
|
||||||
|
|
||||||
|
for (y, row) in rows.iter().enumerate() {
|
||||||
|
let columns: Vec<&str> = row.split(' ').collect();
|
||||||
|
|
||||||
|
for (x, column) in columns.iter().enumerate() {
|
||||||
|
// Create the position at which to create something on the map
|
||||||
|
let position = Position::new(x as u8, y as u8);
|
||||||
|
|
||||||
|
// Figure out which object to create
|
||||||
|
match *column {
|
||||||
|
"." => create_floor(world, position),
|
||||||
|
"W" => {
|
||||||
|
create_floor(world, position);
|
||||||
|
create_wall(world, position);
|
||||||
|
}
|
||||||
|
"P" => {
|
||||||
|
create_floor(world, position);
|
||||||
|
create_player(world, position);
|
||||||
|
}
|
||||||
|
"B" => {
|
||||||
|
create_floor(world, position);
|
||||||
|
create_box(world, position);
|
||||||
|
}
|
||||||
|
"S" => {
|
||||||
|
create_floor(world, position);
|
||||||
|
create_box_spot(world, position);
|
||||||
|
}
|
||||||
|
"N" => (),
|
||||||
|
c => panic!("unrecognized map item {}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
src/resources.rs
Normal file
12
src/resources.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use ggez::event::KeyCode;
|
||||||
|
use specs::World;
|
||||||
|
|
||||||
|
// Resources
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InputQueue {
|
||||||
|
pub keys_pressed: Vec<KeyCode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_resources(world: &mut World) {
|
||||||
|
world.insert(InputQueue::default())
|
||||||
|
}
|
99
src/systems/input_system.rs
Normal file
99
src/systems/input_system.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
use ggez::event::KeyCode;
|
||||||
|
use specs::join::Join;
|
||||||
|
use specs::world::Index;
|
||||||
|
use specs::{Entities, ReadStorage, System, Write, WriteStorage};
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::components::{Immovable, Movable, Player, Position};
|
||||||
|
use crate::constants::{MAP_HEIGHT, MAP_WIDTH};
|
||||||
|
use crate::resources::InputQueue;
|
||||||
|
|
||||||
|
pub struct InputSystem {}
|
||||||
|
|
||||||
|
impl<'a> System<'a> for InputSystem {
|
||||||
|
// Data
|
||||||
|
type SystemData = (
|
||||||
|
Write<'a, InputQueue>,
|
||||||
|
Entities<'a>,
|
||||||
|
WriteStorage<'a, Position>,
|
||||||
|
ReadStorage<'a, Player>,
|
||||||
|
ReadStorage<'a, Movable>,
|
||||||
|
ReadStorage<'a, Immovable>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
let (mut input_queue, entities, mut positions, players, movables, immovables) = data;
|
||||||
|
|
||||||
|
let mut to_move = Vec::new();
|
||||||
|
|
||||||
|
for (position, _player) in (&positions, &players).join() {
|
||||||
|
// Get the first key pressed
|
||||||
|
if let Some(key) = input_queue.keys_pressed.pop() {
|
||||||
|
// 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 {
|
||||||
|
KeyCode::Up => position.y -= 1,
|
||||||
|
KeyCode::Down => position.y += 1,
|
||||||
|
KeyCode::Left => position.x -= 1,
|
||||||
|
KeyCode::Right => position.x += 1,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/systems/mod.rs
Normal file
5
src/systems/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod input_system;
|
||||||
|
mod rendering_system;
|
||||||
|
|
||||||
|
pub use self::input_system::InputSystem;
|
||||||
|
pub use self::rendering_system::RenderingSystem;
|
45
src/systems/rendering_system.rs
Normal file
45
src/systems/rendering_system.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use ggez::graphics::DrawParam;
|
||||||
|
use ggez::graphics::Image;
|
||||||
|
use ggez::nalgebra as na;
|
||||||
|
use ggez::{graphics, Context};
|
||||||
|
use specs::{Join, ReadStorage, System};
|
||||||
|
|
||||||
|
use crate::components::{Position, Renderable};
|
||||||
|
use crate::constants::TILE_WIDTH;
|
||||||
|
|
||||||
|
pub struct RenderingSystem<'a> {
|
||||||
|
pub context: &'a mut Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> System<'a> for RenderingSystem<'a> {
|
||||||
|
type SystemData = (ReadStorage<'a, Position>, ReadStorage<'a, Renderable>);
|
||||||
|
|
||||||
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
let (positions, renderables) = data;
|
||||||
|
|
||||||
|
// Clear the screen/set the background
|
||||||
|
graphics::clear(self.context, graphics::Color::new(0.95, 0.95, 0.95, 1.0));
|
||||||
|
|
||||||
|
// Get all the renderables with their positions and sort by the position z
|
||||||
|
// This will allow us to have entities layered visually.
|
||||||
|
let mut rendering_data = (&positions, &renderables).join().collect::<Vec<_>>();
|
||||||
|
rendering_data.sort_by(|&a, &b| a.0.z.partial_cmp(&b.0.z).expect("expected comparison"));
|
||||||
|
|
||||||
|
// Iterate through all paris of positions & renderables, load the image
|
||||||
|
// and draw it at the specified position.
|
||||||
|
for (position, renderable) in rendering_data.iter() {
|
||||||
|
// Load the image
|
||||||
|
let image = Image::new(self.context, renderable.path.clone()).expect("expected image");
|
||||||
|
let x = position.x as f32 * TILE_WIDTH;
|
||||||
|
let y = position.y as f32 * TILE_WIDTH;
|
||||||
|
|
||||||
|
// draw
|
||||||
|
let draw_params = DrawParam::new().dest(na::Point2::new(x, y));
|
||||||
|
graphics::draw(self.context, &image, draw_params).expect("expected render");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, present the context, this will actually display everything
|
||||||
|
// on the screen.
|
||||||
|
graphics::present(self.context).expect("expected to present");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user