rust-sokoban/src/systems/input_system.rs

124 lines
4.3 KiB
Rust

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::*;
use crate::constants::*;
use crate::events::{EntityMoved, Event};
use crate::resources::{EventQueue, Gameplay, InputQueue};
pub struct InputSystem {}
impl<'a> System<'a> for InputSystem {
// Data
type SystemData = (
Write<'a, EventQueue>,
Write<'a, InputQueue>,
Write<'a, Gameplay>,
Entities<'a>,
WriteStorage<'a, Position>,
ReadStorage<'a, Player>,
ReadStorage<'a, Movable>,
ReadStorage<'a, Immovable>,
);
fn run(&mut self, data: Self::SystemData) {
let (
mut events,
mut input_queue,
mut gameplay,
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
type MovMap = HashMap<(u8, u8), Index>;
let mov: MovMap = (&entities, &movables, &positions)
.join()
.map(|t| ((t.2.x, t.2.y), t.0.id()))
.collect();
let immov: MovMap = (&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<u8> = if start < end {
(start..=end).collect()
} else {
(end..=start).rev().collect()
};
for value in range {
let pos = if is_x {
(value, position.y)
} else {
(position.x, value)
};
// 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)),
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();
events.events.push(Event::PlayerHistObstacle {})
}
None => break,
}
}
}
}
}
}
// We've just moved, so let's increase the number of moves
if to_move.len() > 0 {
gameplay.moves_count += 1;
}
// 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,
_ => (),
}
}
// Fire an event for the entity that just moved
events.events.push(Event::EntityMoved(EntityMoved { id }));
}
}
}