diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 72834cc2f9..83383e5674 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -1,4 +1,4 @@ -use specs::{Component, FlaggedStorage, NullStorage, VecStorage}; +use specs::{Component, FlaggedStorage, VecStorage}; use vek::*; #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs new file mode 100644 index 0000000000..39cd73058b --- /dev/null +++ b/common/src/sys/combat.rs @@ -0,0 +1,80 @@ +use crate::{ + comp::{ + phys::{ForceUpdate, Ori, Pos, Vel}, + Attacking, HealthSource, Stats, + }, + state::{DeltaTime, Uid}, +}; +use log::warn; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; + +/// This system is responsible for handling accepted inputs like moving or attacking +pub struct Sys; +impl<'a> System<'a> for Sys { + type SystemData = ( + Entities<'a>, + ReadStorage<'a, Uid>, + Read<'a, DeltaTime>, + ReadStorage<'a, Pos>, + WriteStorage<'a, Vel>, + ReadStorage<'a, Ori>, + WriteStorage<'a, Attacking>, + WriteStorage<'a, Stats>, + WriteStorage<'a, ForceUpdate>, + ); + + fn run( + &mut self, + ( + entities, + uids, + dt, + positions, + mut velocities, + orientations, + mut attackings, + mut stats, + mut force_updates, + ): Self::SystemData, + ) { + // Attacks + (&entities, &uids, &positions, &orientations, &mut attackings) + .join() + .filter_map(|(entity, uid, pos, ori, mut attacking)| { + if !attacking.applied { + // Go through all other entities + for (b, pos_b, mut vel_b, mut stat_b) in + (&entities, &positions, &mut velocities, &mut stats).join() + { + // Check if it is a hit + if entity != b + && !stat_b.is_dead + && pos.0.distance_squared(pos_b.0) < 50.0 + && ori.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0 + { + // Deal damage + stat_b.hp.change_by(-10, HealthSource::Attack { by: *uid }); // TODO: variable damage and weapon + vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0; + vel_b.0.z = 15.0; + if let Err(err) = force_updates.insert(b, ForceUpdate) { + warn!("Inserting ForceUpdate for an entity failed: {:?}", err); + } + } + } + attacking.applied = true; + } + + if attacking.time > 0.5 { + Some(entity) + } else { + attacking.time += dt.0; + None + } + }) + .collect::>() + .into_iter() + .for_each(|e| { + attackings.remove(e); + }); + } +} diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index 921c4bff23..d32cb7eae4 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -1,16 +1,11 @@ use crate::{ comp::{ phys::{ForceUpdate, Ori, Pos, Vel}, - Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, MoveDir, - OnGround, Respawning, Stats, + Attacking, Controller, Gliding, Jumping, MoveDir, OnGround, Respawning, Stats, }, - state::{DeltaTime, Uid}, - terrain::TerrainMap, - vol::{ReadVol, Vox}, + state::DeltaTime, }; -use log::warn; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; -use vek::*; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; /// This system is responsible for validating controller inputs pub struct Sys; @@ -20,7 +15,6 @@ impl<'a> System<'a> for Sys { Read<'a, DeltaTime>, ReadStorage<'a, Controller>, ReadStorage<'a, Stats>, - ReadExpect<'a, TerrainMap>, ReadStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, @@ -40,7 +34,6 @@ impl<'a> System<'a> for Sys { dt, controllers, stats, - terrain, positions, mut velocities, mut orientations, @@ -50,10 +43,21 @@ impl<'a> System<'a> for Sys { mut attackings, mut respawns, mut glidings, - mut force_updates, + force_updates, ): Self::SystemData, ) { - for (entity, controller, stats, pos, mut vel, mut ori, on_ground) in ( + for ( + entity, + controller, + stats, + pos, + mut vel, + mut ori, + on_ground, + mut attacking, + mut jumping, + mut gliding, + ) in ( &entities, &controllers, &stats, @@ -61,6 +65,9 @@ impl<'a> System<'a> for Sys { &mut velocities, &mut orientations, on_grounds.maybe(), + attackings.maybe(), + jumpings.maybe(), + glidings.maybe(), ) .join() { @@ -73,10 +80,10 @@ impl<'a> System<'a> for Sys { } // Glide - if controller.glide && on_ground.is_none() && attackings.get(entity).is_none() { - glidings.insert(entity, Gliding); + if controller.glide && on_ground.is_none() && attacking.is_none() { + gliding = Some(&Gliding); } else { - glidings.remove(entity); + gliding = None } // Move dir @@ -90,18 +97,15 @@ impl<'a> System<'a> for Sys { ); // Attack - if controller.attack - && attackings.get(entity).is_none() - && glidings.get(entity).is_none() - { - attackings.insert(entity, Attacking::start()); + if controller.attack && attacking.is_none() && gliding.is_none() { + attacking = Some(&Attacking::start()); } // Jump - if on_grounds.get(entity).is_some() && controller.jump && vel.0.z <= 0.0 { - jumpings.insert(entity, Jumping); + if on_ground.is_some() && controller.jump && vel.0.z <= 0.0 { + jumping = Some(&Jumping); } else { - jumpings.remove(entity); + jumping = None; } } } diff --git a/common/src/sys/inputs.rs b/common/src/sys/inputs.rs deleted file mode 100644 index 702328de87..0000000000 --- a/common/src/sys/inputs.rs +++ /dev/null @@ -1,151 +0,0 @@ -use crate::{ - comp::{ - phys::{ForceUpdate, Ori, Pos, Vel}, - Animation, AnimationInfo, Attacking, Gliding, HealthSource, Jumping, MoveDir, OnGround, - Respawning, Stats, - }, - state::{DeltaTime, Uid}, - terrain::TerrainMap, - vol::{ReadVol, Vox}, -}; -use log::warn; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; -use vek::*; - -const HUMANOID_ACCEL: f32 = 100.0; -const HUMANOID_SPEED: f32 = 500.0; -const HUMANOID_AIR_ACCEL: f32 = 10.0; -const HUMANOID_AIR_SPEED: f32 = 100.0; -const HUMANOID_JUMP_ACCEL: f32 = 16.0; -const GLIDE_ACCEL: f32 = 15.0; -const GLIDE_SPEED: f32 = 45.0; -// Gravity is 9.81 * 4, so this makes gravity equal to .15 -const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95; - -/// This system is responsible for handling accepted inputs like moving or attacking -pub struct Sys; -impl<'a> System<'a> for Sys { - type SystemData = ( - Entities<'a>, - ReadStorage<'a, Uid>, - Read<'a, DeltaTime>, - ReadExpect<'a, TerrainMap>, - ReadStorage<'a, Pos>, - ReadStorage<'a, OnGround>, - ReadStorage<'a, MoveDir>, - WriteStorage<'a, Vel>, - WriteStorage<'a, Ori>, - WriteStorage<'a, AnimationInfo>, - WriteStorage<'a, Stats>, - WriteStorage<'a, Respawning>, - WriteStorage<'a, Jumping>, - WriteStorage<'a, Gliding>, - WriteStorage<'a, Attacking>, - WriteStorage<'a, ForceUpdate>, - ); - - fn run( - &mut self, - ( - entities, - uids, - dt, - terrain, - positions, - on_grounds, - move_dirs, - mut velocities, - mut orientations, - mut animation_infos, - mut stats, - mut respawnings, - mut jumpings, - glidings, - mut attackings, - mut force_updates, - ): Self::SystemData, - ) { - // Attacks - (&entities, &uids, &positions, &orientations, &mut attackings) - .join() - .filter_map(|(entity, uid, pos, ori, mut attacking)| { - if !attacking.applied { - // Go through all other entities - for (b, pos_b, stat_b, mut vel_b) in - (&entities, &positions, &mut stats, &mut velocities).join() - { - // Check if it is a hit - if entity != b - && !stat_b.is_dead - && pos.0.distance_squared(pos_b.0) < 50.0 - && ori.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0 - { - // Deal damage - stat_b.hp.change_by(-10, HealthSource::Attack { by: *uid }); // TODO: variable damage and weapon - vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0; - vel_b.0.z = 15.0; - if let Err(err) = force_updates.insert(b, ForceUpdate) { - warn!("Inserting ForceUpdate for an entity failed: {:?}", err); - } - } - } - attacking.applied = true; - } - - if attacking.time > 0.5 { - Some(entity) - } else { - attacking.time += dt.0; - None - } - }) - .collect::>() - .into_iter() - .for_each(|e| { - attackings.remove(e); - }); - - // Apply movement inputs - for (entity, mut vel, mut ori, on_ground, move_dir, jumping, gliding) in ( - &entities, - &mut velocities, - &mut orientations, - on_grounds.maybe(), - move_dirs.maybe(), - jumpings.maybe(), - glidings.maybe(), - ) - .join() - { - // Move player according to move_dir - if let Some(move_dir) = move_dir { - vel.0 += Vec2::broadcast(dt.0) - * move_dir.0 - * match (on_ground.is_some(), gliding.is_some()) { - (true, false) if vel.0.magnitude() < HUMANOID_SPEED => HUMANOID_ACCEL, - (false, true) if vel.0.magnitude() < GLIDE_SPEED => GLIDE_ACCEL, - (false, false) if vel.0.magnitude() < HUMANOID_AIR_SPEED => { - HUMANOID_AIR_ACCEL - } - _ => 0.0, - }; - } - - // Jump - if jumping.is_some() { - vel.0.z = HUMANOID_JUMP_ACCEL; - } - - // Glide - if gliding.is_some() && vel.0.magnitude() < GLIDE_SPEED && vel.0.z < 0.0 { - let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2; - vel.0.z += dt.0 * anti_grav * Vec2::::from(vel.0 * 0.15).magnitude().min(1.0); - } - - // Set direction based on velocity - if vel.0.magnitude_squared() != 0.0 { - ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0); - } - } - } -} diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index 97861dea9f..65b24f12fa 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -1,7 +1,7 @@ pub mod agent; pub mod animation; +pub mod combat; pub mod controller; -pub mod inputs; pub mod phys; mod stats; @@ -12,7 +12,7 @@ use specs::DispatcherBuilder; const AGENT_SYS: &str = "agent_sys"; const CONTROLLER_SYS: &str = "controller_sys"; const PHYS_SYS: &str = "phys_sys"; -const INPUTS_SYS: &str = "inputs_sys"; +const COMBAT_SYS: &str = "combat_sys"; const ANIMATION_SYS: &str = "animation_sys"; const STATS_SYS: &str = "stats_sys"; @@ -20,7 +20,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[]); dispatch_builder.add(phys::Sys, PHYS_SYS, &[]); - dispatch_builder.add(inputs::Sys, INPUTS_SYS, &[]); + dispatch_builder.add(combat::Sys, COMBAT_SYS, &[]); dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[]); - dispatch_builder.add(stats::Sys, STATS_SYS, &[INPUTS_SYS]); + dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]); } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 270f276964..1fa7d7c848 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - phys::{Pos, Vel}, - OnGround, Stats, + phys::{Ori, Pos, Vel}, + Gliding, Jumping, MoveDir, OnGround, Stats, }, state::DeltaTime, terrain::TerrainMap, @@ -13,6 +13,15 @@ use vek::*; const GRAVITY: f32 = 9.81 * 4.0; const FRIC_GROUND: f32 = 0.15; const FRIC_AIR: f32 = 0.015; +const HUMANOID_ACCEL: f32 = 100.0; +const HUMANOID_SPEED: f32 = 500.0; +const HUMANOID_AIR_ACCEL: f32 = 10.0; +const HUMANOID_AIR_SPEED: f32 = 100.0; +const HUMANOID_JUMP_ACCEL: f32 = 16.0; +const GLIDE_ACCEL: f32 = 15.0; +const GLIDE_SPEED: f32 = 45.0; +// Gravity is 9.81 * 4, so this makes gravity equal to .15 +const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95; // Integrates forces, calculates the new velocity based off of the old velocity // dt = delta time @@ -35,30 +44,89 @@ fn integrate_forces(dt: f32, mut lv: Vec3, damp: f32) -> Vec3 { lv } -/// This system applies forces and calculates new positions and velocities +/// This system applies forces and calculates new positions and velocities. pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, ReadExpect<'a, TerrainMap>, Read<'a, DeltaTime>, - ReadStorage<'a, Stats>, + WriteStorage<'a, OnGround>, WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, - WriteStorage<'a, OnGround>, + WriteStorage<'a, Ori>, + ReadStorage<'a, MoveDir>, + ReadStorage<'a, Jumping>, + ReadStorage<'a, Gliding>, + WriteStorage<'a, Stats>, ); fn run( &mut self, - (entities, terrain, dt, stats, mut positions, mut velocities, mut on_grounds): Self::SystemData, + ( + entities, + terrain, + dt, + mut on_grounds, + mut positions, + mut velocities, + mut orientations, + move_dirs, + jumpings, + glidings, + stats, + ): Self::SystemData, ) { - for (entity, stats, pos, vel) in (&entities, &stats, &mut positions, &mut velocities).join() + // Apply movement inputs + for (entity, mut pos, mut vel, mut ori, mut on_ground, move_dir, jumping, gliding, stats) in + ( + &entities, + &mut positions, + &mut velocities, + &mut orientations, + on_grounds.maybe(), + move_dirs.maybe(), + jumpings.maybe(), + glidings.maybe(), + &stats, + ) + .join() { - // Disable while dead TODO: Replace with client states + // Disable while dead TODO: Replace with client states? if stats.is_dead { continue; } + // Move player according to move_dir + if let Some(move_dir) = move_dir { + vel.0 += Vec2::broadcast(dt.0) + * move_dir.0 + * match (on_ground.is_some(), gliding.is_some()) { + (true, false) if vel.0.magnitude() < HUMANOID_SPEED => HUMANOID_ACCEL, + (false, true) if vel.0.magnitude() < GLIDE_SPEED => GLIDE_ACCEL, + (false, false) if vel.0.magnitude() < HUMANOID_AIR_SPEED => { + HUMANOID_AIR_ACCEL + } + _ => 0.0, + }; + } + + // Jump + if jumping.is_some() { + vel.0.z = HUMANOID_JUMP_ACCEL; + } + + // Glide + if gliding.is_some() && vel.0.magnitude() < GLIDE_SPEED && vel.0.z < 0.0 { + let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2; + vel.0.z += dt.0 * lift * Vec2::::from(vel.0 * 0.15).magnitude().min(1.0); + } + + // Set direction based on velocity + if vel.0.magnitude_squared() != 0.0 { + ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0); + } + // Movement pos.0 += vel.0 * dt.0; @@ -69,15 +137,15 @@ impl<'a> System<'a> for Sys { .unwrap_or(false) && vel.0.z <= 0.0 { - on_grounds.insert(entity, OnGround); + on_ground = Some(&OnGround); } else { - on_grounds.remove(entity); + on_ground = None; } // Integrate forces // Friction is assumed to be a constant dependent on location let friction = 50.0 - * if on_grounds.get(entity).is_some() { + * if on_ground.is_some() { FRIC_GROUND } else { FRIC_AIR diff --git a/diff b/diff deleted file mode 100644 index 4f91ddd46b..0000000000 --- a/diff +++ /dev/null @@ -1,578 +0,0 @@ -diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs -index f68a5b4..8ebd46f 100644 ---- a/chat-cli/src/main.rs -+++ b/chat-cli/src/main.rs -@@ -29,7 +29,7 @@ fn main() { - client.send_chat("Hello!".to_string()); - - loop { -- let events = match client.tick(comp::Control::default(), clock.get_last_delta()) { -+ let events = match client.tick(comp::Controller::default(), clock.get_last_delta()) { - Ok(events) => events, - Err(err) => { - error!("Error: {:?}", err); -diff --git a/client/src/lib.rs b/client/src/lib.rs -index f491359..ee3b62e 100644 ---- a/client/src/lib.rs -+++ b/client/src/lib.rs -@@ -154,52 +154,6 @@ impl Client { - self.postbox.send_message(ClientMsg::Chat(msg)) - } - -- /// Jump locally, the new positions will be synced to the server -- #[allow(dead_code)] -- pub fn jump(&mut self) { -- if self.client_state != ClientState::Character { -- return; -- } -- self.state.write_component(self.entity, comp::Jumping); -- } -- -- /// Start to glide locally, animation will be synced -- #[allow(dead_code)] -- 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 - #[allow(dead_code)] - pub fn clear_terrain(&mut self) { -@@ -209,7 +163,7 @@ impl Client { - - /// Execute a single client tick, handle input and update the game state by the given duration. - #[allow(dead_code)] -- pub fn tick(&mut self, control: comp::Control, dt: Duration) -> Result, Error> { -+ pub fn tick(&mut self, controller: comp::Controller, 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 -@@ -226,10 +180,8 @@ 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! -- if self.client_state == ClientState::Character { -- self.state.write_component(self.entity, control.clone()); -- } -+ self.state.write_component(self.entity, controller.clone()); -+ self.postbox.send_message(ClientMsg::Controller(controller)); - - // 2) Build up a list of events for this frame, to be passed to the frontend. - let mut frontend_events = Vec::new(); -diff --git a/common/src/comp/inputs.rs b/common/src/comp/inputs.rs -index ce55110..27b7b75 100644 ---- a/common/src/comp/inputs.rs -+++ b/common/src/comp/inputs.rs -@@ -1,11 +1,6 @@ - use specs::{Component, FlaggedStorage, NullStorage, VecStorage}; - use vek::*; - --#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] --pub struct Control { -- pub move_dir: Vec2, --} -- - #[derive(Clone, Debug, Default, Serialize, Deserialize)] - pub struct Respawning; - -@@ -14,16 +9,13 @@ pub struct Attacking { - pub time: f32, - pub applied: bool, - } -+ - #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] - pub struct Jumping; - - #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] - pub struct Gliding; - --impl Component for Control { -- type Storage = VecStorage; --} -- - impl Component for Respawning { - type Storage = NullStorage; - } -@@ -36,6 +28,7 @@ impl Attacking { - } - } - } -+ - impl Component for Attacking { - type Storage = FlaggedStorage>; - } -diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs -index ee351db..2ca2e12 100644 ---- a/common/src/comp/mod.rs -+++ b/common/src/comp/mod.rs -@@ -2,6 +2,7 @@ pub mod actor; - pub mod agent; - pub mod animation; - pub mod inputs; -+pub mod controller; - pub mod phys; - pub mod player; - pub mod stats; -@@ -15,8 +16,8 @@ pub use actor::QuadrupedMediumBody; - pub use agent::Agent; - pub use animation::Animation; - pub use animation::AnimationInfo; -+pub use controller::Controller; - pub use inputs::Attacking; --pub use inputs::Control; - pub use inputs::Gliding; - pub use inputs::Jumping; - pub use inputs::Respawning; -diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs -index aa765d0..1187831 100644 ---- a/common/src/msg/client.rs -+++ b/common/src/msg/client.rs -@@ -11,8 +11,7 @@ pub enum ClientMsg { - name: String, - body: comp::Body, - }, -- Attack, -- Respawn, -+ Controller(comp::Controller), - RequestState(ClientState), - SetViewDistance(u32), - Ping, -diff --git a/common/src/state.rs b/common/src/state.rs -index 9357c60..630f7c8 100644 ---- a/common/src/state.rs -+++ b/common/src/state.rs -@@ -110,9 +110,9 @@ impl State { - ecs.register::(); - ecs.register::(); - ecs.register::(); -+ ecs.register::(); - - // Register client-local components -- ecs.register::(); - ecs.register::(); - - // Register server-local components -diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs -index 55e845c..0e5e2f8 100644 ---- a/common/src/sys/agent.rs -+++ b/common/src/sys/agent.rs -@@ -1,4 +1,4 @@ --use crate::comp::{phys::Pos, Agent, Attacking, Control, Jumping}; -+use crate::comp::{phys::Pos, Agent, Attacking, Controller, Jumping}; - use log::warn; - use rand::{seq::SliceRandom, thread_rng}; - use specs::{Entities, Join, ReadStorage, System, WriteStorage}; -@@ -12,17 +12,17 @@ impl<'a> System<'a> for Sys { - Entities<'a>, - WriteStorage<'a, Agent>, - ReadStorage<'a, Pos>, -- WriteStorage<'a, Control>, -+ WriteStorage<'a, Controller>, - WriteStorage<'a, Jumping>, - WriteStorage<'a, Attacking>, - ); - - fn run( - &mut self, -- (entities, mut agents, positions, mut controls, mut jumps, mut attacks): Self::SystemData, -+ (entities, mut agents, positions, mut controllers, mut jumps, mut attacks): Self::SystemData, - ) { -- for (entity, agent, pos, control) in -- (&entities, &mut agents, &positions, &mut controls).join() -+ for (entity, agent, pos, controller) in -+ (&entities, &mut agents, &positions, &mut controllers).join() - { - match agent { - Agent::Wanderer(bearing) => { -@@ -32,7 +32,7 @@ impl<'a> System<'a> for Sys { - - pos.0 * 0.0002; - - if bearing.magnitude_squared() != 0.0 { -- control.move_dir = bearing.normalized(); -+ controller.move_dir = bearing.normalized(); - } - } - Agent::Pet { target, offset } => { -@@ -49,7 +49,7 @@ impl<'a> System<'a> for Sys { - - // Move towards the target. - let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude(); -- control.move_dir = if dist > 5.0 { -+ controller.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() -@@ -57,7 +57,7 @@ impl<'a> System<'a> for Sys { - Vec2::zero() - }; - } -- _ => control.move_dir = Vec2::zero(), -+ _ => controller.move_dir = Vec2::zero(), - } - - // Change offset occasionally. -@@ -72,7 +72,7 @@ impl<'a> System<'a> for Sys { - Some(tgt_pos) => { - let dist = Vec2::::from(tgt_pos.0 - pos.0).magnitude(); - if dist < 2.0 { -- control.move_dir = Vec2::zero(); -+ controller.move_dir = Vec2::zero(); - - if rand::random::() < 0.2 { - attacks -@@ -82,7 +82,7 @@ impl<'a> System<'a> for Sys { - - false - } else if dist < 60.0 { -- control.move_dir = -+ controller.move_dir = - Vec2::::from(tgt_pos.0 - pos.0).normalized() * 0.96; - - false -@@ -91,7 +91,7 @@ impl<'a> System<'a> for Sys { - } - } - None => { -- control.move_dir = Vec2::one(); -+ controller.move_dir = Vec2::one(); - true - } - }; -diff --git a/common/src/sys/inputs.rs b/common/src/sys/inputs.rs -index 288a0e0..6cadba5 100644 ---- a/common/src/sys/inputs.rs -+++ b/common/src/sys/inputs.rs -@@ -1,7 +1,7 @@ - use crate::{ - comp::{ - phys::{ForceUpdate, Ori, Pos, Vel}, -- Animation, AnimationInfo, Attacking, Control, Gliding, HealthSource, Jumping, Stats, -+ Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, Stats, - }, - state::{DeltaTime, Uid}, - terrain::TerrainMap, -@@ -13,17 +13,6 @@ use vek::*; - - // Basic ECS AI agent system - pub struct Sys; -- --const HUMANOID_ACCEL: f32 = 100.0; --const HUMANOID_SPEED: f32 = 500.0; --const HUMANOID_AIR_ACCEL: f32 = 10.0; --const HUMANOID_AIR_SPEED: f32 = 100.0; --const HUMANOID_JUMP_ACCEL: f32 = 16.0; --const GLIDE_ACCEL: f32 = 15.0; --const GLIDE_SPEED: f32 = 45.0; --// Gravity is 9.81 * 4, so this makes gravity equal to .15 --const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95; -- - impl<'a> System<'a> for Sys { - type SystemData = ( - Entities<'a>, -@@ -35,7 +24,7 @@ impl<'a> System<'a> for Sys { - WriteStorage<'a, Ori>, - WriteStorage<'a, AnimationInfo>, - WriteStorage<'a, Stats>, -- ReadStorage<'a, Control>, -+ ReadStorage<'a, Controller>, - WriteStorage<'a, Jumping>, - WriteStorage<'a, Gliding>, - WriteStorage<'a, Attacking>, -@@ -54,67 +43,31 @@ impl<'a> System<'a> for Sys { - mut orientations, - mut animation_infos, - mut stats, -- controls, -+ controllers, - mut jumps, - glides, - mut attacks, - mut force_updates, - ): Self::SystemData, - ) { -- for (entity, pos, control, stats, mut ori, mut vel) in ( -+ for (entity, pos, controller, stats, mut ori, mut vel) in ( - &entities, - &positions, -- &controls, -+ &controllers, - &stats, - &mut orientations, - &mut velocities, - ) - .join() - { -- // Disable while dead TODO: Replace with client states -- if stats.is_dead { -- continue; -- } -- - let on_ground = terrain - .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32)) - .map(|vox| !vox.is_empty()) - .unwrap_or(false) - && vel.0.z <= 0.0; - -- let gliding = glides.get(entity).is_some() && vel.0.z < 0.0; -- let move_dir = if control.move_dir.magnitude() > 1.0 { -- control.move_dir.normalized() -- } else { -- control.move_dir -- }; -- -- if on_ground { -- // Move player according to move_dir -- if vel.0.magnitude() < HUMANOID_SPEED { -- vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_ACCEL; -- } -- -- // Jump -- if jumps.get(entity).is_some() && vel.0.z <= 0.0 { -- vel.0.z = HUMANOID_JUMP_ACCEL; -- jumps.remove(entity); -- } -- } else if gliding && vel.0.magnitude() < GLIDE_SPEED { -- let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2; -- vel.0.z += dt.0 * anti_grav * Vec2::::from(vel.0 * 0.15).magnitude().min(1.0); -- vel.0 += Vec2::broadcast(dt.0) * move_dir * GLIDE_ACCEL; -- } else if vel.0.magnitude() < HUMANOID_AIR_SPEED { -- vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_AIR_ACCEL; -- } -- -- // Set direction based on velocity -- if vel.0.magnitude_squared() != 0.0 { -- ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0); -- } -- - let animation = if on_ground { -- if control.move_dir.magnitude() > 0.01 { -+ if controller.move_dir.magnitude() > 0.01 { - Animation::Run - } else if attacks.get(entity).is_some() { - Animation::Attack -@@ -149,6 +102,7 @@ impl<'a> System<'a> for Sys { - (&entities, &uids, &positions, &orientations, &mut attacks).join() - { - if !attacking.applied { -+ dbg!(); - for (b, pos_b, stat_b, mut vel_b) in - (&entities, &positions, &mut stats, &mut velocities).join() - { -diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs -index da6f629..e6a5917 100644 ---- a/common/src/sys/mod.rs -+++ b/common/src/sys/mod.rs -@@ -1,6 +1,7 @@ - pub mod actions; - pub mod agent; - pub mod animation; -+pub mod controller; - pub mod inputs; - pub mod phys; - mod stats; -@@ -10,6 +11,7 @@ use specs::DispatcherBuilder; - - // System names - const AGENT_SYS: &str = "agent_sys"; -+const CONTROLLER_SYS: &str = "controller_sys"; - const INPUTS_SYS: &str = "inputs_sys"; - const ACTIONS_SYS: &str = "actions_sys"; - const PHYS_SYS: &str = "phys_sys"; -@@ -20,6 +22,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { - dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); - dispatch_builder.add(phys::Sys, PHYS_SYS, &[]); - dispatch_builder.add(actions::Sys, ACTIONS_SYS, &[]); -+ dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[]); - dispatch_builder.add(inputs::Sys, INPUTS_SYS, &[]); - dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[]); - dispatch_builder.add(stats::Sys, STATS_SYS, &[INPUTS_SYS]); -diff --git a/server/src/lib.rs b/server/src/lib.rs -index ead806c..6260f0a 100644 ---- a/server/src/lib.rs -+++ b/server/src/lib.rs -@@ -145,7 +145,7 @@ impl Server { - .with(pos) - .with(comp::phys::Vel(Vec3::zero())) - .with(comp::phys::Ori(Vec3::unit_y())) -- .with(comp::Control::default()) -+ .with(comp::Controller::default()) - .with(comp::AnimationInfo::default()) - .with(comp::Actor::Character { name, body }) - .with(comp::Stats::default()) -@@ -164,6 +164,7 @@ impl Server { - state.write_component(entity, comp::Actor::Character { name, body }); - state.write_component(entity, comp::Stats::default()); - state.write_component(entity, comp::AnimationInfo::default()); -+ state.write_component(entity, comp::Controller::default()); - state.write_component(entity, comp::phys::Pos(spawn_point)); - state.write_component(entity, comp::phys::Vel(Vec3::zero())); - state.write_component(entity, comp::phys::Ori(Vec3::unit_y())); -@@ -492,25 +493,16 @@ impl Server { - } - ClientState::Pending => {} - }, -- ClientMsg::Attack => match client.client_state { -- ClientState::Character => { -- if state -- .ecs() -- .read_storage::() -- .get(entity) -- .is_none() -- { -- state.write_component(entity, comp::Attacking::start()); -- } -+ ClientMsg::Controller(controller) => match client.client_state { -+ ClientState::Connected | ClientState::Registered | ClientState::Spectator => { -+ client.error_state(RequestStateError::Impossible) - } -- _ => client.error_state(RequestStateError::Impossible), -- }, -- ClientMsg::Respawn => match client.client_state { -- ClientState::Dead => { -- state.write_component(entity, comp::Respawning); -+ ClientState::Dead -+ | ClientState::Character => { -+ state.write_component(entity, controller); - } -- _ => client.error_state(RequestStateError::Impossible), -- }, -+ ClientState::Pending => {} -+ } - ClientMsg::Chat(msg) => match client.client_state { - ClientState::Connected => { - client.error_state(RequestStateError::Impossible) -diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs -index 2192ce7..1a3e3e3 100644 ---- a/voxygen/src/menu/char_selection/mod.rs -+++ b/voxygen/src/menu/char_selection/mod.rs -@@ -110,7 +110,7 @@ impl PlayState for CharSelectionState { - if let Err(err) = self - .client - .borrow_mut() -- .tick(comp::Control::default(), clock.get_last_delta()) -+ .tick(comp::Controller::default(), clock.get_last_delta()) - { - error!("Failed to tick the scene: {:?}", err); - return PlayStateResult::Pop; -diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs -index 913e421..bd5d876 100644 ---- a/voxygen/src/session.rs -+++ b/voxygen/src/session.rs -@@ -18,8 +18,9 @@ const FPS: u64 = 60; - pub struct SessionState { - scene: Scene, - client: Rc>, -- key_state: KeyState, - hud: Hud, -+ key_state: KeyState, -+ controller: comp::Controller, - } - - /// Represents an active game session (i.e., the one being played). -@@ -32,6 +33,7 @@ impl SessionState { - scene, - client, - key_state: KeyState::new(), -+ controller: comp::Controller::default(), - hud: Hud::new(window), - } - } -@@ -47,21 +49,11 @@ const BG_COLOR: Rgba = Rgba { - - impl SessionState { - /// Tick the session (and the client attached to it). -- pub fn tick(&mut self, dt: Duration) -> Result<(), Error> { -- // Calculate the movement input vector of the player from the current key presses -- // and the camera direction. -- let ori = self.scene.camera().get_orientation(); -- let unit_vecs = ( -- Vec2::new(ori[0].cos(), -ori[0].sin()), -- Vec2::new(ori[0].sin(), ori[0].cos()), -- ); -- let dir_vec = self.key_state.dir_vec(); -- let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1]; -- -+ fn tick(&mut self, dt: Duration) -> Result<(), Error> { - for event in self - .client - .borrow_mut() -- .tick(comp::Control { move_dir }, dt)? -+ .tick(self.controller.clone(), dt)? - { - match event { - client::Event::Chat(msg) => { -@@ -121,19 +113,19 @@ impl PlayState for SessionState { - Event::Close => { - return PlayStateResult::Shutdown; - } -- Event::InputUpdate(GameInput::Attack, true) => { -- self.client.borrow_mut().attack(); -- self.client.borrow_mut().respawn(); -+ Event::InputUpdate(GameInput::Attack, state) => { -+ self.controller.attack = state; -+ self.controller.respawn = state; // TODO: Don't do both - } -- Event::InputUpdate(GameInput::Jump, true) => { -- self.client.borrow_mut().jump(); -+ Event::InputUpdate(GameInput::Jump, state) => { -+ self.controller.jump = state; - } - 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.client.borrow_mut().glide(state) -+ self.controller.glide = state; - } - - // Pass all other events to the scene -@@ -143,6 +135,17 @@ impl PlayState for SessionState { - } - } - -+ // Calculate the movement input vector of the player from the current key presses -+ // and the camera direction. -+ let ori = self.scene.camera().get_orientation(); -+ let unit_vecs = ( -+ Vec2::new(ori[0].cos(), -ori[0].sin()), -+ Vec2::new(ori[0].sin(), ori[0].cos()), -+ ); -+ let dir_vec = self.key_state.dir_vec(); -+ self.controller.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1]; -+ -+ - // Perform an in-game tick. - if let Err(err) = self.tick(clock.get_last_delta()) { - error!("Failed to tick the scene: {:?}", err);