diff --git a/client/src/lib.rs b/client/src/lib.rs index 7a07d6b501..02d024d0b4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -70,9 +70,6 @@ impl Client { _ => return Err(Error::ServerWentMad), }; - // Initialize ecs components the client has actions over - state.write_component(entity, comp::Inputs::default()); - Ok(Self { client_state, thread_pool: threadpool::Builder::new() @@ -94,11 +91,13 @@ impl Client { }) } + /// Request a state transition to `ClientState::Registered`. pub fn register(&mut self, player: comp::Player) { self.postbox.send_message(ClientMsg::Register { player }); self.client_state = ClientState::Pending; } + /// Request a state transition to `ClientState::Character`. pub fn request_character(&mut self, name: String, body: comp::Body) { self.postbox .send_message(ClientMsg::Character { name, body }); @@ -111,24 +110,56 @@ impl Client { .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail } - /// Get a reference to the client's worker thread pool. This pool should be used for any - /// computationally expensive operations that run outside of the main thread (i.e., threads that - /// block on I/O operations are exempt). + /// Send a chat message to the server. #[allow(dead_code)] - pub fn thread_pool(&self) -> &threadpool::ThreadPool { - &self.thread_pool + pub fn send_chat(&mut self, msg: String) { + self.postbox.send_message(ClientMsg::Chat(msg)) } - /// Get a reference to the client's game state. + /// Jump locally, the new positions will be synced to the server #[allow(dead_code)] - pub fn state(&self) -> &State { - &self.state + pub fn jump(&mut self) { + if self.client_state != ClientState::Character { + return; + } + self.state.write_component(self.entity, comp::Jumping); } - /// Get a mutable reference to the client's game state. + /// Start to glide locally, animation will be synced #[allow(dead_code)] - pub fn state_mut(&mut self) -> &mut State { - &mut self.state + pub fn glide(&mut self, state: bool) { + if self.client_state != ClientState::Character { + return; + } + if state { + self.state.write_component(self.entity, comp::Gliding); + } else { + self.state + .ecs_mut() + .write_storage::() + .remove(self.entity); + } + } + + /// Start to attack + #[allow(dead_code)] + pub fn attack(&mut self) { + if self.client_state != ClientState::Character { + return; + } + // TODO: Test if attack is possible using timeout + self.state + .write_component(self.entity, comp::Attacking::start()); + self.postbox.send_message(ClientMsg::Attack); + } + + /// Tell the server the client wants to respawn. + #[allow(dead_code)] + pub fn respawn(&mut self) { + if self.client_state != ClientState::Dead { + return; + } + self.postbox.send_message(ClientMsg::Respawn) } /// Remove all cached terrain @@ -138,38 +169,9 @@ impl Client { self.pending_chunks.clear(); } - /// Get the player's entity. - #[allow(dead_code)] - pub fn entity(&self) -> EcsEntity { - self.entity - } - - /// Get the client state - #[allow(dead_code)] - pub fn get_client_state(&self) -> ClientState { - self.client_state - } - - /// Get the current tick number. - #[allow(dead_code)] - pub fn get_tick(&self) -> u64 { - self.tick - } - - /// Send a chat message to the server. - #[allow(dead_code)] - pub fn send_chat(&mut self, msg: String) { - self.postbox.send_message(ClientMsg::Chat(msg)) - } - - #[allow(dead_code)] - pub fn get_ping_ms(&self) -> f64 { - self.last_ping_delta * 1000.0 - } - /// Execute a single client tick, handle input and update the game state by the given duration. #[allow(dead_code)] - pub fn tick(&mut self, input: comp::Inputs, dt: Duration) -> Result, Error> { + pub fn tick(&mut self, control: comp::Control, dt: Duration) -> Result, Error> { // This tick function is the centre of the Veloren universe. Most client-side things are // managed from here, and as such it's important that it stays organised. Please consult // the core developers before making significant changes to this code. Here is the @@ -187,11 +189,9 @@ impl Client { // 1) Handle input from frontend. // Pass character actions from frontend input to the player's entity. // TODO: Only do this if the entity already has a Inputs component! - self.state.write_component(self.entity, input.clone()); - - // Tell the server about the inputs. - self.postbox - .send_message(ClientMsg::PlayerInputs(input.clone())); + if self.client_state == ClientState::Character { + self.state.write_component(self.entity, control.clone()); + } // 2) Build up a list of events for this frame, to be passed to the frontend. let mut frontend_events = Vec::new(); @@ -288,6 +288,25 @@ impl Client { } // 7) Finish the tick, pass control back to the frontend. + + // Cleanup + self.state + .ecs_mut() + .write_storage::() + .remove(self.entity); + self.state + .ecs_mut() + .write_storage::() + .remove(self.entity); + self.state + .ecs_mut() + .write_storage::() + .remove(self.entity); + self.state + .ecs_mut() + .write_storage::() + .remove(self.entity); + self.tick += 1; Ok(frontend_events) } @@ -378,6 +397,49 @@ impl Client { Ok(frontend_events) } + + /// Get the player's entity. + #[allow(dead_code)] + pub fn entity(&self) -> EcsEntity { + self.entity + } + + /// Get the client state + #[allow(dead_code)] + pub fn get_client_state(&self) -> ClientState { + self.client_state + } + + /// Get the current tick number. + #[allow(dead_code)] + pub fn get_tick(&self) -> u64 { + self.tick + } + + #[allow(dead_code)] + pub fn get_ping_ms(&self) -> f64 { + self.last_ping_delta * 1000.0 + } + + /// Get a reference to the client's worker thread pool. This pool should be used for any + /// computationally expensive operations that run outside of the main thread (i.e., threads that + /// block on I/O operations are exempt). + #[allow(dead_code)] + pub fn thread_pool(&self) -> &threadpool::ThreadPool { + &self.thread_pool + } + + /// Get a reference to the client's game state. + #[allow(dead_code)] + pub fn state(&self) -> &State { + &self.state + } + + /// Get a mutable reference to the client's game state. + #[allow(dead_code)] + pub fn state_mut(&mut self) -> &mut State { + &mut self.state + } } impl Drop for Client { diff --git a/common/src/comp/inputs.rs b/common/src/comp/inputs.rs index 1e83462a9b..ce55110ce0 100644 --- a/common/src/comp/inputs.rs +++ b/common/src/comp/inputs.rs @@ -1,33 +1,49 @@ -use specs::{Component, FlaggedStorage, VecStorage}; +use specs::{Component, FlaggedStorage, NullStorage, VecStorage}; use vek::*; -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum InputEvent { - Jump, - Attack, - RequestRespawn, +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Control { + pub move_dir: Vec2, } +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Respawning; + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] -pub struct Inputs { - // Held down - pub move_dir: Vec2, - pub jumping: bool, - pub gliding: bool, - - // Event based - pub events: Vec, +pub struct Attacking { + pub time: f32, + pub applied: bool, } +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Jumping; -impl Component for Inputs { +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Gliding; + +impl Component for Control { type Storage = VecStorage; } -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] -pub struct Actions { - pub attack_time: Option, +impl Component for Respawning { + type Storage = NullStorage; } -impl Component for Actions { +impl Attacking { + pub fn start() -> Self { + Self { + time: 0.0, + applied: false, + } + } +} +impl Component for Attacking { type Storage = FlaggedStorage>; } + +impl Component for Jumping { + type Storage = NullStorage; +} + +impl Component for Gliding { + type Storage = NullStorage; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index d9612e853f..03fb57f617 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -14,10 +14,11 @@ pub use actor::QuadrupedBody; pub use agent::Agent; pub use animation::Animation; pub use animation::AnimationInfo; -pub use inputs::Actions; -pub use inputs::InputEvent; -pub use inputs::Inputs; +pub use inputs::Attacking; +pub use inputs::Control; +pub use inputs::Gliding; +pub use inputs::Jumping; +pub use inputs::Respawning; pub use player::Player; -pub use player::Respawn; pub use stats::Dying; pub use stats::Stats; diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 3901867ebc..a3c6ee49d1 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -21,6 +21,12 @@ pub struct Stats { pub xp: u32, } +impl Stats { + pub fn is_dead(&self) -> bool { + self.hp.current == 0 + } +} + impl Default for Stats { fn default() -> Self { Self { diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index df36cd3f06..b340b1797b 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -11,12 +11,13 @@ pub enum ClientMsg { name: String, body: comp::Body, }, + Attack, + Respawn, RequestState(ClientState), SetViewDistance(u32), Ping, Pong, Chat(String), - PlayerInputs(comp::Inputs), PlayerAnimation(comp::AnimationInfo), PlayerPhysics { pos: comp::phys::Pos, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index b83fd2da54..c4e3163d62 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -23,7 +23,7 @@ sphynx::sum_type! { Actor(comp::Actor), Player(comp::Player), Stats(comp::Stats), - Actions(comp::Actions), + Attacking(comp::Attacking), } } // Automatically derive From for EcsCompPhantom @@ -37,7 +37,7 @@ sphynx::sum_type! { Actor(PhantomData), Player(PhantomData), Stats(PhantomData), - Actions(PhantomData), + Attacking(PhantomData), } } impl sphynx::CompPacket for EcsCompPacket { diff --git a/common/src/state.rs b/common/src/state.rs index c5bf86485c..61278edca3 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -103,7 +103,7 @@ impl State { ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); - ecs.register_synced::(); + ecs.register_synced::(); ecs.register::(); // Register unsynced (or synced by other means) components. @@ -111,9 +111,12 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); ecs.register::(); - ecs.register::(); ecs.register::(); ecs.register::(); @@ -192,7 +195,8 @@ impl State { /// Removes every chunk of the terrain. pub fn clear_terrain(&mut self) { - let keys = self.terrain_mut() + let keys = self + .terrain_mut() .drain() .map(|(key, _)| key) .collect::>(); diff --git a/common/src/sys/actions.rs b/common/src/sys/actions.rs index 14b2660514..05613761f4 100644 --- a/common/src/sys/actions.rs +++ b/common/src/sys/actions.rs @@ -6,7 +6,7 @@ use vek::*; use crate::{ comp::{ phys::{Dir, Pos, Vel}, - Actions, Animation, AnimationInfo, + Animation, AnimationInfo, Attacking, }, state::DeltaTime, terrain::TerrainMap, @@ -17,18 +17,27 @@ use crate::{ pub struct Sys; impl<'a> System<'a> for Sys { - type SystemData = (Entities<'a>, Read<'a, DeltaTime>, WriteStorage<'a, Actions>); + type SystemData = ( + Entities<'a>, + Read<'a, DeltaTime>, + WriteStorage<'a, Attacking>, + ); - fn run(&mut self, (entities, dt, mut actions): Self::SystemData) { - for (entity, mut action) in (&entities, &mut actions).join() { - let should_end = action.attack_time.as_mut().map_or(false, |mut time| { - *time += dt.0; - *time > 0.5 // TODO: constant - }); + fn run(&mut self, (entities, dt, mut attackings): Self::SystemData) { + for (entity, attacking) in (&entities, &mut attackings).join() { + attacking.time += dt.0; + } - if should_end { - action.attack_time = None; - } + let finished_attack = (&entities, &mut attackings) + .join() + .filter(|(e, a)| { + a.time > 0.5 // TODO: constant + }) + .map(|(e, a)| e) + .collect::>(); + + for entity in finished_attack { + attackings.remove(entity); } } } diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index fdc5644e41..ea6bc8c320 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -1,22 +1,29 @@ // Library -use specs::{Join, Read, ReadStorage, System, WriteStorage}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; use vek::*; // Crate -use crate::comp::{phys::Pos, Agent, Inputs}; +use crate::comp::{phys::Pos, Agent, Control, Jumping}; // Basic ECS AI agent system pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( + Entities<'a>, WriteStorage<'a, Agent>, ReadStorage<'a, Pos>, - WriteStorage<'a, Inputs>, + WriteStorage<'a, Control>, + WriteStorage<'a, Jumping>, ); - fn run(&mut self, (mut agents, positions, mut inputs): Self::SystemData) { - for (mut agent, pos, mut input) in (&mut agents, &positions, &mut inputs).join() { + fn run( + &mut self, + (entities, mut agents, positions, mut controls, mut jumpings): Self::SystemData, + ) { + for (entity, agent, pos, control) in + (&entities, &mut agents, &positions, &mut controls).join() + { match agent { Agent::Wanderer(bearing) => { *bearing += Vec2::new(rand::random::() - 0.5, rand::random::() - 0.5) @@ -25,7 +32,7 @@ impl<'a> System<'a> for Sys { - pos.0 * 0.0002; if bearing.magnitude_squared() != 0.0 { - input.move_dir = bearing.normalized(); + control.move_dir = bearing.normalized(); } } Agent::Pet { target, offset } => { @@ -34,12 +41,13 @@ impl<'a> System<'a> for Sys { Some(tgt_pos) => { let tgt_pos = tgt_pos.0 + *offset; - // Jump with target. - input.jumping = tgt_pos.z > pos.0.z + 1.0; + if tgt_pos.z > pos.0.z + 1.0 { + jumpings.insert(entity, Jumping); + } // Move towards the target. let dist = tgt_pos.distance(pos.0); - input.move_dir = if dist > 5.0 { + control.move_dir = if dist > 5.0 { Vec2::from(tgt_pos - pos.0).normalized() } else if dist < 1.5 && dist > 0.0 { Vec2::from(pos.0 - tgt_pos).normalized() @@ -47,7 +55,7 @@ impl<'a> System<'a> for Sys { Vec2::zero() }; } - _ => input.move_dir = Vec2::zero(), + _ => control.move_dir = Vec2::zero(), } // Change offset occasionally. diff --git a/common/src/sys/inputs.rs b/common/src/sys/inputs.rs index 41ee1885c4..6f0a4f267b 100644 --- a/common/src/sys/inputs.rs +++ b/common/src/sys/inputs.rs @@ -6,7 +6,7 @@ use vek::*; use crate::{ comp::{ phys::{Dir, ForceUpdate, Pos, Vel}, - Actions, Animation, AnimationInfo, InputEvent, Inputs, Respawn, Stats, + Animation, AnimationInfo, Attacking, Control, Gliding, Jumping, Respawning, Stats, }, state::{DeltaTime, Time}, terrain::TerrainMap, @@ -22,14 +22,16 @@ impl<'a> System<'a> for Sys { Read<'a, Time>, Read<'a, DeltaTime>, ReadExpect<'a, TerrainMap>, - WriteStorage<'a, Inputs>, - WriteStorage<'a, Actions>, ReadStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Dir>, WriteStorage<'a, AnimationInfo>, WriteStorage<'a, Stats>, - WriteStorage<'a, Respawn>, + ReadStorage<'a, Control>, + ReadStorage<'a, Jumping>, + WriteStorage<'a, Respawning>, + WriteStorage<'a, Gliding>, + WriteStorage<'a, Attacking>, WriteStorage<'a, ForceUpdate>, ); @@ -40,27 +42,29 @@ impl<'a> System<'a> for Sys { time, dt, terrain, - mut inputs, - mut actions, positions, mut velocities, mut directions, mut animation_infos, mut stats, - mut respawns, + mut controls, + mut jumpings, + mut respawnings, + mut glidings, + mut attackings, mut force_updates, ): Self::SystemData, ) { - for (entity, inputs, pos, mut dir, mut vel) in ( + for (entity, pos, control, mut dir, mut vel) in ( &entities, - &mut inputs, &positions, + &controls, &mut directions, &mut velocities, ) .join() { - // Handle held-down inputs + // Handle held-down control let on_ground = terrain .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32)) .map(|vox| !vox.is_empty()) @@ -70,9 +74,9 @@ impl<'a> System<'a> for Sys { let (gliding, friction) = if on_ground { // TODO: Don't hard-code this. // Apply physics to the player: acceleration and non-linear deceleration. - vel.0 += Vec2::broadcast(dt.0) * inputs.move_dir * 200.0; + vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 200.0; - if inputs.jumping { + if jumpings.get(entity).is_some() { vel.0.z += 16.0; } @@ -80,9 +84,9 @@ impl<'a> System<'a> for Sys { } else { // TODO: Don't hard-code this. // Apply physics to the player: acceleration and non-linear deceleration. - vel.0 += Vec2::broadcast(dt.0) * inputs.move_dir * 10.0; + vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 10.0; - if inputs.gliding && vel.0.z < 0.0 { + if glidings.get(entity).is_some() && vel.0.z < 0.0 { // TODO: Don't hard-code this. let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2; vel.0.z += @@ -109,12 +113,12 @@ impl<'a> System<'a> for Sys { } let animation = if on_ground { - if inputs.move_dir.magnitude() > 0.01 { + if control.move_dir.magnitude() > 0.01 { Animation::Run } else { Animation::Idle } - } else if gliding { + } else if glidings.get(entity).is_some() { Animation::Gliding } else { Animation::Jump @@ -135,42 +139,27 @@ impl<'a> System<'a> for Sys { }, ); } - for (entity, inputs) in (&entities, &mut inputs).join() { - // Handle event-based inputs - for event in inputs.events.drain(..) { - match event { - InputEvent::Attack => { - // Attack delay - if let (Some(pos), Some(dir), Some(action)) = ( - positions.get(entity), - directions.get(entity), - actions.get_mut(entity), - ) { - for (b, pos_b, mut stat_b, mut vel_b) in - (&entities, &positions, &mut stats, &mut velocities).join() - { - // Check if it is a hit - if entity != b - && pos.0.distance_squared(pos_b.0) < 50.0 - && dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0 - { - // Set action - action.attack_time = Some(0.0); - // Deal damage - stat_b.hp.change_by(-10); // TODO: variable damage - vel_b.0 += (pos_b.0 - pos.0).normalized() * 20.0; - vel_b.0.z = 20.0; - force_updates.insert(b, ForceUpdate); - } - } - } + for (entity, pos, dir, attacking) in + (&entities, &positions, &directions, &mut attackings).join() + { + if !attacking.applied { + for (b, pos_b, mut stat_b, mut vel_b) in + (&entities, &positions, &mut stats, &mut velocities).join() + { + // Check if it is a hit + if entity != b + && pos.0.distance_squared(pos_b.0) < 50.0 + && dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0 + { + // Deal damage + stat_b.hp.change_by(-10); // TODO: variable damage + vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0; + vel_b.0.z = 15.0; + force_updates.insert(b, ForceUpdate); } - InputEvent::RequestRespawn => { - respawns.insert(entity, Respawn); - } - InputEvent::Jump => {} } + attacking.applied = true; } } } diff --git a/common/src/volumes/vol_map_2d.rs b/common/src/volumes/vol_map_2d.rs index 1cc45fefc2..b552778a04 100644 --- a/common/src/volumes/vol_map_2d.rs +++ b/common/src/volumes/vol_map_2d.rs @@ -6,7 +6,11 @@ use crate::{ dyna::{Dyna, DynaErr}, }, }; -use std::{collections::{hash_map, HashMap}, marker::PhantomData, sync::Arc}; +use std::{ + collections::{hash_map, HashMap}, + marker::PhantomData, + sync::Arc, +}; use vek::*; #[derive(Debug)] diff --git a/server/src/cmd.rs b/server/src/cmd.rs index d6ec525fde..93533b3b23 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -141,7 +141,10 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch fn handle_kill(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { server .state - .write_component::(entity, comp::Dying) + .ecs_mut() + .write_storage::() + .get_mut(entity) + .map(|s| s.hp.current = 0); } fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { @@ -208,9 +211,11 @@ fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &Cha .state .read_component_cloned::(entity) { - Some(pos) => { + Some(mut pos) => { + pos.0.x += 1.0; server .create_npc( + pos, "Bungo".to_owned(), comp::Body::Quadruped(comp::QuadrupedBody::random()), ) @@ -218,7 +223,6 @@ fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &Cha target: entity, offset: Vec2::zero(), }) - .with(pos) .build(); server .clients diff --git a/server/src/lib.rs b/server/src/lib.rs index c8aed3e6d5..99f3d118b8 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -126,15 +126,19 @@ impl Server { /// Build a non-player character. #[allow(dead_code)] - pub fn create_npc(&mut self, name: String, body: comp::Body) -> EcsEntityBuilder { + pub fn create_npc( + &mut self, + pos: comp::phys::Pos, + name: String, + body: comp::Body, + ) -> EcsEntityBuilder { self.state .ecs_mut() .create_entity_synced() - .with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0))) + .with(pos) + .with(comp::Control::default()) .with(comp::phys::Vel(Vec3::zero())) .with(comp::phys::Dir(Vec3::unit_y())) - .with(comp::Inputs::default()) - .with(comp::Actions::default()) .with(comp::Actor::Character { name, body }) .with(comp::Stats::default()) } @@ -150,8 +154,7 @@ impl Server { state.write_component(entity, comp::Actor::Character { name, body }); state.write_component(entity, comp::Stats::default()); - state.write_component(entity, comp::Inputs::default()); - state.write_component(entity, comp::Actions::default()); + state.write_component(entity, comp::Control::default()); state.write_component(entity, comp::AnimationInfo::new()); state.write_component(entity, comp::phys::Pos(spawn_point)); state.write_component(entity, comp::phys::Vel(Vec3::zero())); @@ -210,25 +213,17 @@ impl Server { .collect::>(); for entity in todo_kill { - self.state - .ecs_mut() - .write_storage::() - .remove(entity); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); if let Some(client) = self.clients.get_mut(&entity) { client.force_state(ClientState::Dead); } else { - //self.state.ecs_mut().delete_entity_synced(entity); + self.state.ecs_mut().delete_entity_synced(entity); } } // Handle respawns let todo_respawn = ( &self.state.ecs().entities(), - &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), ) .join() .map(|(entity, _)| entity) @@ -237,12 +232,7 @@ impl Server { for entity in todo_respawn { if let Some(client) = self.clients.get_mut(&entity) { client.allow_state(ClientState::Character); - self.state - .ecs_mut() - .write_storage::() - .remove(entity); self.state.write_component(entity, comp::Stats::default()); - self.state.write_component(entity, comp::Actions::default()); self.state .ecs_mut() .write_storage::() @@ -319,6 +309,16 @@ impl Server { self.sync_clients(); // 7) Finish the tick, pass control back to the frontend. + + // Cleanup + let ecs = self.state.ecs_mut(); + for entity in ecs.entities().join() { + ecs.write_storage::().remove(entity); + ecs.write_storage::().remove(entity); + ecs.write_storage::().remove(entity); + ecs.write_storage::().remove(entity); + } + Ok(frontend_events) } @@ -442,6 +442,18 @@ impl Server { } ClientState::Pending => {} }, + ClientMsg::Attack => match client.client_state { + ClientState::Character => { + state.write_component(entity, comp::Attacking::start()); + } + _ => client.error_state(RequestStateError::Impossible), + }, + ClientMsg::Respawn => match client.client_state { + ClientState::Dead => { + state.write_component(entity, comp::Respawning); + } + _ => client.error_state(RequestStateError::Impossible), + }, ClientMsg::Chat(msg) => match client.client_state { ClientState::Connected => { client.error_state(RequestStateError::Impossible) @@ -452,18 +464,6 @@ impl Server { | ClientState::Character => new_chat_msgs.push((entity, msg)), ClientState::Pending => {} }, - ClientMsg::PlayerInputs(mut inputs) => match client.client_state { - ClientState::Character | ClientState::Dead => { - state - .ecs_mut() - .write_storage::() - .get_mut(entity) - .map(|s| { - s.events.append(&mut inputs.events); - }); - } - _ => client.error_state(RequestStateError::Impossible), - }, ClientMsg::PlayerAnimation(animation_info) => { match client.client_state { ClientState::Character => { diff --git a/voxygen/src/key_state.rs b/voxygen/src/key_state.rs index 909f1f3490..2921c4ce6c 100644 --- a/voxygen/src/key_state.rs +++ b/voxygen/src/key_state.rs @@ -5,8 +5,6 @@ pub struct KeyState { pub left: bool, pub up: bool, pub down: bool, - pub jump: bool, - pub glide: bool, } impl KeyState { @@ -16,8 +14,6 @@ impl KeyState { left: false, up: false, down: false, - jump: false, - glide: false, } } diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 1563f2e017..c0c84d3fdf 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -109,7 +109,7 @@ impl PlayState for CharSelectionState { // Tick the client (currently only to keep the connection alive). self.client .borrow_mut() - .tick(comp::Inputs::default(), clock.get_last_delta()) + .tick(comp::Control::default(), clock.get_last_delta()) .expect("Failed to tick the client"); self.client.borrow_mut().cleanup(); diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 09db7596b2..75e3633d35 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -366,7 +366,8 @@ impl FigureMgr { .and_then(|stats| stats.hp.last_change) .map(|(change_by, time)| { Rgba::broadcast(1.0) - + Rgba::new(0.0, -1.0, -1.0, 0.0).map(|c| (c / (1.0 + DAMAGE_FADE_COEFFICIENT * time)) as f32) + + Rgba::new(0.0, -1.0, -1.0, 0.0) + .map(|c| (c / (1.0 + DAMAGE_FADE_COEFFICIENT * time)) as f32) }) .unwrap_or(Rgba::broadcast(1.0)); @@ -460,13 +461,17 @@ impl FigureMgr { let tick = client.get_tick(); let ecs = client.state().ecs(); - for (entity, actor, _) in ( + for (entity, actor, stats) in ( &ecs.entities(), &ecs.read_storage::(), - &ecs.read_storage::(), + &ecs.read_storage::(), // Just to make sure the entity is alive ) .join() { + if stats.is_dead() { + return; + } + match actor { comp::Actor::Character { body, .. } => { if let Some((locals, bone_consts)) = match body { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index dbaa93db75..ca04c4f4d0 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -19,7 +19,6 @@ pub struct SessionState { scene: Scene, client: Rc>, key_state: KeyState, - input_events: Vec, hud: Hud, } @@ -34,7 +33,6 @@ impl SessionState { client, key_state: KeyState::new(), hud: Hud::new(window), - input_events: Vec::new(), } } } @@ -60,19 +58,11 @@ impl SessionState { let dir_vec = self.key_state.dir_vec(); let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1]; - // Take the input events. - let mut input_events = Vec::new(); - mem::swap(&mut self.input_events, &mut input_events); - - for event in self.client.borrow_mut().tick( - comp::Inputs { - move_dir, - jumping: self.key_state.jump, - gliding: self.key_state.glide, - events: input_events, - }, - dt, - )? { + for event in self + .client + .borrow_mut() + .tick(comp::Control { move_dir }, dt)? + { match event { client::Event::Chat(msg) => { self.hud.new_message(msg); @@ -136,21 +126,19 @@ impl PlayState for SessionState { return PlayStateResult::Shutdown; } // Attack key pressed - Event::InputUpdate(GameInput::Attack, state) => { - self.input_events.push(comp::InputEvent::Attack); - //self.input_events.push(comp::InputEvent::RequestRespawn); - }, + Event::InputUpdate(GameInput::Attack, true) => { + self.client.borrow_mut().attack(); + self.client.borrow_mut().respawn(); + } + Event::InputUpdate(GameInput::Jump, true) => { + self.client.borrow_mut().jump(); + } Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state, Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state, Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state, Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = state, - Event::InputUpdate(GameInput::Glide, state) => self.key_state.glide = state, - Event::InputUpdate(GameInput::Jump, true) => { - self.input_events.push(comp::InputEvent::Jump); - self.key_state.jump = true; - } - Event::InputUpdate(GameInput::Jump, false) => { - self.key_state.jump = false; + Event::InputUpdate(GameInput::Glide, state) => { + self.client.borrow_mut().glide(state) } // Pass all other events to the scene diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 46ac859517..aa01158954 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -113,12 +113,18 @@ impl Window { key_map.insert(settings.controls.map, GameInput::Map); key_map.insert(settings.controls.bag, GameInput::Bag); key_map.insert(settings.controls.quest_log, GameInput::QuestLog); - key_map.insert(settings.controls.character_window, GameInput::CharacterWindow); + key_map.insert( + settings.controls.character_window, + GameInput::CharacterWindow, + ); key_map.insert(settings.controls.social, GameInput::Social); key_map.insert(settings.controls.spellbook, GameInput::Spellbook); key_map.insert(settings.controls.settings, GameInput::Settings); key_map.insert(settings.controls.help, GameInput::Help); - key_map.insert(settings.controls.toggle_interface, GameInput::ToggleInterface); + key_map.insert( + settings.controls.toggle_interface, + GameInput::ToggleInterface, + ); key_map.insert(settings.controls.toggle_debug, GameInput::ToggleDebug); key_map.insert(settings.controls.fullscreen, GameInput::Fullscreen); key_map.insert(settings.controls.screenshot, GameInput::Screenshot); @@ -184,11 +190,12 @@ impl Window { events.push(Event::Resize(Vec2::new(width as u32, height as u32))); } glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)), - glutin::WindowEvent::MouseInput { button, state, .. } - if cursor_grabbed => - { + glutin::WindowEvent::MouseInput { button, state, .. } if cursor_grabbed => { if let Some(&game_input) = key_map.get(&KeyMouse::Mouse(button)) { - events.push(Event::InputUpdate(game_input, state == glutin::ElementState::Pressed)) + events.push(Event::InputUpdate( + game_input, + state == glutin::ElementState::Pressed, + )) } } glutin::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode @@ -204,7 +211,10 @@ impl Window { glutin::ElementState::Pressed => take_screenshot = true, _ => {} }, - Some(&game_input) => events.push(Event::InputUpdate(game_input, input.state == glutin::ElementState::Pressed)), + Some(&game_input) => events.push(Event::InputUpdate( + game_input, + input.state == glutin::ElementState::Pressed, + )), _ => {} }, _ => {}