use std::sync::mpsc;
use specs;
use specs::{ReadStorage, WriteStorage, Fetch, FetchMut};
use slog::Logger;
use piston::input::Input;
use types::*;
use super::{
CellDweller,
ActiveCellDweller,
SendMessageQueue,
CellDwellerMessage,
SetPosMessage,
};
use Spatial;
use movement::*;
use globe::Globe;
use globe::chunk::Material;
use input_adapter;
use ::net::{
SendMessage,
Transport,
Destination,
NetMarker,
};
pub struct MovementInputAdapter {
sender: mpsc::Sender<MovementEvent>,
}
impl MovementInputAdapter {
pub fn new(sender: mpsc::Sender<MovementEvent>) -> MovementInputAdapter {
MovementInputAdapter { sender: sender }
}
}
impl input_adapter::InputAdapter for MovementInputAdapter {
fn handle(&self, input_event: &Input) {
use piston::input::{Button, ButtonState};
use piston::input::keyboard::Key;
if let &Input::Button(button_args) = input_event {
if let Button::Keyboard(key) = button_args.button {
let is_down = match button_args.state {
ButtonState::Press => true,
ButtonState::Release => false,
};
match key {
Key::I => self.sender.send(MovementEvent::StepForward(is_down)).unwrap(),
Key::K => self.sender.send(MovementEvent::StepBackward(is_down)).unwrap(),
Key::J => self.sender.send(MovementEvent::TurnLeft(is_down)).unwrap(),
Key::L => self.sender.send(MovementEvent::TurnRight(is_down)).unwrap(),
_ => (),
}
}
}
}
}
pub enum MovementEvent {
StepForward(bool),
StepBackward(bool),
TurnLeft(bool),
TurnRight(bool),
}
pub struct MovementSystem {
input_receiver: mpsc::Receiver<MovementEvent>,
log: Logger,
step_forward: bool,
step_backward: bool,
turn_left: bool,
turn_right: bool,
max_step_height: u8,
}
enum ForwardOrBackward {
Forward,
Backward,
}
impl MovementSystem {
pub fn new(
world: &mut specs::World,
input_receiver: mpsc::Receiver<MovementEvent>,
parent_log: &Logger,
) -> MovementSystem {
use ::AutoResource;
SendMessageQueue::ensure(world);
MovementSystem {
input_receiver: input_receiver,
log: parent_log.new(o!()),
step_forward: false,
step_backward: false,
turn_left: false,
turn_right: false,
max_step_height: 1,
}
}
pub fn init(&mut self, world: &mut specs::World) {
ActiveCellDweller::ensure_registered(world);
}
pub fn set_step_height(&mut self, new_max_step_height: u8) {
self.max_step_height = new_max_step_height;
}
fn consume_input(&mut self) {
loop {
match self.input_receiver.try_recv() {
Ok(MovementEvent::StepForward(b)) => self.step_forward = b,
Ok(MovementEvent::StepBackward(b)) => self.step_backward = b,
Ok(MovementEvent::TurnLeft(b)) => self.turn_left = b,
Ok(MovementEvent::TurnRight(b)) => self.turn_right = b,
Err(_) => return,
}
}
}
fn step_if_possible(
&self,
cd: &mut CellDweller,
globe: &Globe,
forward_or_backward: ForwardOrBackward,
) {
if cd.pos.z < 0 {
return;
}
let under_pos = cd.pos.with_z(cd.pos.z - 1);
let under_cell = globe.maybe_non_authoritative_cell(under_pos);
if under_cell.material != Material::Dirt {
return;
}
let mut new_pos = cd.pos;
let mut new_dir = cd.dir;
let mut new_last_turn_bias = cd.last_turn_bias;
match forward_or_backward {
ForwardOrBackward::Forward => {
step_forward_and_face_neighbor(
&mut new_pos,
&mut new_dir,
globe.spec().root_resolution,
&mut new_last_turn_bias,
)
}
ForwardOrBackward::Backward => {
step_backward_and_face_neighbor(
&mut new_pos,
&mut new_dir,
globe.spec().root_resolution,
&mut new_last_turn_bias,
)
}
}.expect("CellDweller should have been in good state.");
for _ in 0..(self.max_step_height + 1) {
let cell = globe.maybe_non_authoritative_cell(new_pos);
let can_move_to_cell = cell.material != Material::Dirt;
if !can_move_to_cell {
new_pos.z += 1;
continue;
}
cd.set_cell_transform(new_pos, new_dir, new_last_turn_bias);
cd.seconds_until_next_move = cd.seconds_between_moves;
trace!(self.log, "Stepped"; "new_pos" => format!("{:?}", cd.pos()), "new_dir" => format!("{:?}", cd.dir()));
break;
}
}
}
impl<'a> specs::System<'a> for MovementSystem {
type SystemData = (
Fetch<'a, TimeDeltaResource>,
WriteStorage<'a, CellDweller>,
WriteStorage<'a, Spatial>,
ReadStorage<'a, Globe>,
Fetch<'a, ActiveCellDweller>,
FetchMut<'a, SendMessageQueue>,
ReadStorage<'a, NetMarker>,
);
fn run(&mut self, data: Self::SystemData) {
self.consume_input();
let (
dt,
mut cell_dwellers,
mut spatials,
globes,
active_cell_dweller_resource,
mut send_message_queue,
net_markers,
) = data;
let active_cell_dweller_entity = match active_cell_dweller_resource.maybe_entity {
Some(entity) => entity,
None => return,
};
let cd = cell_dwellers.get_mut(active_cell_dweller_entity).expect(
"Someone deleted the controlled entity's CellDweller",
);
let spatial = spatials.get_mut(active_cell_dweller_entity).expect(
"Someone deleted the controlled entity's Spatial",
);
let globe_entity = match cd.globe_entity {
Some(globe_entity) => globe_entity,
None => {
warn!(
self.log,
"There was no associated globe entity or it wasn't actually a Globe! Can't proceed!"
);
return;
}
};
let globe = match globes.get(globe_entity) {
Some(globe) => globe,
None => {
warn!(
self.log,
"The globe associated with this CellDweller is not alive! Can't proceed!"
);
return;
}
};
if cd.seconds_until_next_move > 0.0 {
cd.seconds_until_next_move = (cd.seconds_until_next_move - dt.0).max(0.0);
}
let still_waiting_to_move = cd.seconds_until_next_move > 0.0;
let forward_xor_backward = self.step_forward != self.step_backward;
if !still_waiting_to_move && forward_xor_backward {
let forward_or_backward = if self.step_forward {
ForwardOrBackward::Forward
} else {
ForwardOrBackward::Backward
};
self.step_if_possible(cd, globe, forward_or_backward);
}
if cd.seconds_until_next_turn > 0.0 {
cd.seconds_until_next_turn = (cd.seconds_until_next_turn - dt.0).max(0.0);
}
let still_waiting_to_turn = cd.seconds_until_next_turn > 0.0;
if !still_waiting_to_turn {
if self.turn_left && !self.turn_right {
cd.turn(TurnDir::Left);
cd.seconds_until_next_turn = cd.seconds_between_turns;
trace!(self.log, "Turned left"; "new_pos" => format!("{:?}", cd.pos()), "new_dir" => format!("{:?}", cd.dir()));
} else if self.turn_right && !self.turn_left {
cd.turn(TurnDir::Right);
cd.seconds_until_next_turn = cd.seconds_between_turns;
trace!(self.log, "Turned right"; "new_pos" => format!("{:?}", cd.pos()), "new_dir" => format!("{:?}", cd.dir()));
}
}
if cd.is_real_space_transform_dirty() {
if send_message_queue.has_consumer {
let entity_id = net_markers
.get(active_cell_dweller_entity)
.expect("Shouldn't be trying to tell peers about entities that don't have global IDs!")
.id;
send_message_queue.queue.push_back(
SendMessage {
destination: Destination::EveryoneElse,
game_message: CellDwellerMessage::SetPos(SetPosMessage {
entity_id: entity_id,
new_pos: cd.pos,
new_dir: cd.dir,
new_last_turn_bias: cd.last_turn_bias,
}),
transport: Transport::UDP,
}
)
}
spatial.set_local_transform(cd.get_real_transform_and_mark_as_clean());
}
}
}