diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index fdf8607bf9..1a4f099579 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 6d4da2a5e7..0d50cb51a6 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -153,7 +153,7 @@ impl Client { pub fn current_chunk(&self) -> Option> { let chunk_pos = Vec2::from( self.state - .read_storage::() + .read_storage::() .get(self.entity) .cloned()? .0, @@ -171,52 +171,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) { @@ -226,7 +180,11 @@ 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 @@ -243,10 +201,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(); @@ -262,7 +218,7 @@ impl Client { // 5) Terrain let pos = self .state - .read_storage::() + .read_storage::() .get(self.entity) .cloned(); if let (Some(pos), Some(view_distance)) = (pos, self.view_distance) { @@ -336,19 +292,6 @@ impl Client { _ => {} } - // Update the server about the player's current animation. - if let Some(animation_info) = self - .state - .ecs_mut() - .write_storage::() - .get_mut(self.entity) - { - if animation_info.changed { - self.postbox - .send_message(ClientMsg::PlayerAnimation(animation_info.clone())); - } - } - // Output debug metrics if log_enabled!(log::Level::Info) && self.tick % 600 == 0 { let metrics = self diff --git a/common/src/comp/animation.rs b/common/src/comp/animation.rs index 85ec2e1453..d3e89dc18c 100644 --- a/common/src/comp/animation.rs +++ b/common/src/comp/animation.rs @@ -7,6 +7,9 @@ pub enum Animation { Jump, Gliding, Attack, + Roll, + Crun, + Cidle, } #[derive(Copy, Clone, Debug, Serialize, Deserialize)] diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs new file mode 100644 index 0000000000..7cd5eba3a4 --- /dev/null +++ b/common/src/comp/controller.rs @@ -0,0 +1,16 @@ +use specs::{Component, FlaggedStorage, VecStorage}; +use vek::*; + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Controller { + pub move_dir: Vec2, + pub jump: bool, + pub attack: bool, + pub roll: bool, + pub glide: bool, + pub respawn: bool, +} + +impl Component for Controller { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/inputs.rs b/common/src/comp/inputs.rs index ce55110ce0..66c51d634b 100644 --- a/common/src/comp/inputs.rs +++ b/common/src/comp/inputs.rs @@ -1,29 +1,33 @@ 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; +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct MoveDir(pub Vec2); + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct Attacking { pub time: f32, pub applied: bool, } + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Rolling { + pub time: f32, + pub applied: bool, +} + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct OnGround; + #[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,14 +40,36 @@ impl Attacking { } } } + +impl Rolling { + pub fn start() -> Self { + Self { + time: 0.0, + applied: false, + } + } +} + +impl Component for MoveDir { + type Storage = VecStorage; +} + impl Component for Attacking { type Storage = FlaggedStorage>; } +impl Component for Rolling { + type Storage = FlaggedStorage>; +} + +impl Component for OnGround { + type Storage = NullStorage; +} + impl Component for Jumping { type Storage = NullStorage; } impl Component for Gliding { - type Storage = NullStorage; + type Storage = FlaggedStorage>; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index ee351db610..9f1c20f910 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,26 +1,18 @@ pub mod actor; -pub mod agent; -pub mod animation; -pub mod inputs; -pub mod phys; -pub mod player; -pub mod stats; +mod agent; +mod animation; +mod controller; +mod inputs; +mod phys; +mod player; +mod stats; // Reexports -pub use actor::Actor; -pub use actor::Body; -pub use actor::HumanoidBody; -pub use actor::QuadrupedBody; -pub use actor::QuadrupedMediumBody; +pub use actor::{Actor, Body, HumanoidBody, QuadrupedBody, QuadrupedMediumBody}; pub use agent::Agent; -pub use animation::Animation; -pub use animation::AnimationInfo; -pub use inputs::Attacking; -pub use inputs::Control; -pub use inputs::Gliding; -pub use inputs::Jumping; -pub use inputs::Respawning; +pub use animation::{Animation, AnimationInfo}; +pub use controller::Controller; +pub use inputs::{Attacking, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling}; +pub use phys::{ForceUpdate, Ori, Pos, Vel}; pub use player::Player; -pub use stats::Dying; -pub use stats::HealthSource; -pub use stats::Stats; +pub use stats::{Dying, HealthSource, Stats}; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index adfc64ffe0..6fe1e603bb 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -2,7 +2,6 @@ use specs::{Component, NullStorage, VecStorage}; use vek::*; // Position - #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct Pos(pub Vec3); @@ -11,7 +10,6 @@ impl Component for Pos { } // Velocity - #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct Vel(pub Vec3); @@ -20,7 +18,6 @@ impl Component for Vel { } // Orientation - #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct Ori(pub Vec3); @@ -29,7 +26,6 @@ impl Component for Ori { } // ForceUpdate - #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] pub struct ForceUpdate; diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index aa765d0a52..db1c40d4c7 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -11,18 +11,16 @@ pub enum ClientMsg { name: String, body: comp::Body, }, - Attack, - Respawn, + Controller(comp::Controller), RequestState(ClientState), SetViewDistance(u32), Ping, Pong, Chat(String), - PlayerAnimation(comp::AnimationInfo), PlayerPhysics { - pos: comp::phys::Pos, - vel: comp::phys::Vel, - ori: comp::phys::Ori, + pos: comp::Pos, + vel: comp::Vel, + ori: comp::Ori, }, TerrainChunkRequest { key: Vec2, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 5d860ae85c..841b0d5a8e 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -17,13 +17,15 @@ impl sphynx::ResPacket for EcsResPacket {} sphynx::sum_type! { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum EcsCompPacket { - Pos(comp::phys::Pos), - Vel(comp::phys::Vel), - Ori(comp::phys::Ori), + Pos(comp::Pos), + Vel(comp::Vel), + Ori(comp::Ori), Actor(comp::Actor), Player(comp::Player), Stats(comp::Stats), Attacking(comp::Attacking), + Rolling(comp::Rolling), + Gliding(comp::Gliding), } } // Automatically derive From for EcsCompPhantom @@ -31,13 +33,15 @@ sphynx::sum_type! { sphynx::sum_type! { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum EcsCompPhantom { - Pos(PhantomData), - Vel(PhantomData), - Ori(PhantomData), + Pos(PhantomData), + Vel(PhantomData), + Ori(PhantomData), Actor(PhantomData), Player(PhantomData), Stats(PhantomData), Attacking(PhantomData), + Rolling(PhantomData), + Gliding(PhantomData), } } impl sphynx::CompPacket for EcsCompPacket { diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index c82f94b2a1..f3604fb839 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -32,9 +32,9 @@ pub enum ServerMsg { EcsSync(sphynx::SyncPackage), EntityPhysics { entity: u64, - pos: comp::phys::Pos, - vel: comp::phys::Vel, - ori: comp::phys::Ori, + pos: comp::Pos, + vel: comp::Vel, + ori: comp::Ori, }, EntityAnimation { entity: u64, diff --git a/common/src/state.rs b/common/src/state.rs index 9357c60a28..974c841258 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -98,28 +98,31 @@ impl State { // Create a new Sphynx ECS world. fn setup_sphynx_world(ecs: &mut sphynx::World) { - // Register synced components. + // Register server->client synced components. ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); - ecs.register_synced::(); // TODO: Don't send this to the client? - ecs.register::(); + ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); // Register components synced by other means - ecs.register::(); - ecs.register::(); - ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); ecs.register::(); + ecs.register::(); // Register client-local components - ecs.register::(); ecs.register::(); // Register server-local components ecs.register::(); ecs.register::(); - ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); // Register synced resources used by the ECS. diff --git a/common/src/sys/actions.rs b/common/src/sys/actions.rs deleted file mode 100644 index 8e091c5612..0000000000 --- a/common/src/sys/actions.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::{comp::Attacking, state::DeltaTime}; -use specs::{Entities, Join, Read, System, WriteStorage}; - -// Basic ECS AI agent system -pub struct Sys; - -impl<'a> System<'a> for Sys { - type SystemData = ( - Entities<'a>, - Read<'a, DeltaTime>, - WriteStorage<'a, Attacking>, - ); - - fn run(&mut self, (entities, dt, mut attacks): Self::SystemData) { - for attack in (&mut attacks).join() { - attack.time += dt.0; - } - - let finished_attacks = (&entities, &mut attacks) - .join() - .filter(|(_, a)| a.time > 0.25) // TODO: constant - .map(|(e, _)| e) - .collect::>(); - - for entity in finished_attacks { - attacks.remove(entity); - } - } -} diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 55e845ca80..3ff07be4ab 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -1,28 +1,27 @@ -use crate::comp::{phys::Pos, Agent, Attacking, Control, Jumping}; +use crate::comp::{Agent, Attacking, Controller, Jumping, Pos}; use log::warn; use rand::{seq::SliceRandom, thread_rng}; use specs::{Entities, Join, ReadStorage, System, WriteStorage}; use vek::*; -// Basic ECS AI agent system +/// This system will allow NPCs to modify their controller pub struct Sys; - impl<'a> System<'a> for Sys { type SystemData = ( 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 +31,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 +48,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 +56,7 @@ impl<'a> System<'a> for Sys { Vec2::zero() }; } - _ => control.move_dir = Vec2::zero(), + _ => controller.move_dir = Vec2::zero(), } // Change offset occasionally. @@ -72,7 +71,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 +81,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 +90,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/animation.rs b/common/src/sys/animation.rs index 09e194667d..95bb562f90 100644 --- a/common/src/sys/animation.rs +++ b/common/src/sys/animation.rs @@ -1,15 +1,87 @@ -use crate::{comp::AnimationInfo, state::DeltaTime}; -use specs::{Join, Read, System, WriteStorage}; +use crate::{ + comp::{ + Animation, AnimationInfo, Attacking, ForceUpdate, Gliding, Jumping, OnGround, Ori, Pos, + Rolling, Vel, + }, + state::DeltaTime, +}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; -// Basic ECS AI agent system +/// This system will apply the animation that fits best to the users actions pub struct Sys; - impl<'a> System<'a> for Sys { - type SystemData = (Read<'a, DeltaTime>, WriteStorage<'a, AnimationInfo>); + type SystemData = ( + Entities<'a>, + Read<'a, DeltaTime>, + ReadStorage<'a, Vel>, + ReadStorage<'a, OnGround>, + ReadStorage<'a, Jumping>, + ReadStorage<'a, Gliding>, + ReadStorage<'a, Attacking>, + ReadStorage<'a, Rolling>, + WriteStorage<'a, AnimationInfo>, + ); - fn run(&mut self, (dt, mut animation_infos): Self::SystemData) { - for mut animation_info in (&mut animation_infos).join() { + fn run( + &mut self, + ( + entities, + dt, + velocities, + on_grounds, + jumpings, + glidings, + attackings, + rollings, + mut animation_infos, + ): Self::SystemData, + ) { + for (entity, vel, on_ground, jumping, gliding, attacking, rolling, mut animation_info) in ( + &entities, + &velocities, + on_grounds.maybe(), + jumpings.maybe(), + glidings.maybe(), + attackings.maybe(), + rollings.maybe(), + &mut animation_infos, + ) + .join() + { animation_info.time += dt.0 as f64; + + fn impossible_animation(message: &str) -> Animation { + warn!("{}", message); + Animation::Idle + } + + let animation = match ( + on_ground.is_some(), + vel.0.magnitude() > 3.0, // Moving + attacking.is_some(), + gliding.is_some(), + rolling.is_some(), + ) { + (_, _, true, true, _) => impossible_animation("Attack while gliding"), + (_, _, true, _, true) => impossible_animation("Roll while attacking"), + (_, _, _, true, true) => impossible_animation("Roll while gliding"), + (_, false, _, _, true) => impossible_animation("Roll without moving"), + (_, true, false, false, true) => Animation::Roll, + (true, false, false, false, false) => Animation::Idle, + (true, true, false, false, false) => Animation::Run, + (false, _, false, false, false) => Animation::Jump, + (_, _, false, true, false) => Animation::Gliding, + (_, _, true, false, false) => Animation::Attack, + }; + + let last = animation_info.clone(); + let changed = last.animation != animation; + + *animation_info = AnimationInfo { + animation, + time: if changed { 0.0 } else { last.time }, + changed, + }; } } } diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs new file mode 100644 index 0000000000..5aefa31c0d --- /dev/null +++ b/common/src/sys/combat.rs @@ -0,0 +1,77 @@ +use crate::{ + comp::{ + Attacking, HealthSource, Stats, {ForceUpdate, Ori, Pos, Vel}, + }, + 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>, + ReadStorage<'a, Ori>, + WriteStorage<'a, Vel>, + WriteStorage<'a, Attacking>, + WriteStorage<'a, Stats>, + WriteStorage<'a, ForceUpdate>, + ); + + fn run( + &mut self, + ( + entities, + uids, + dt, + positions, + orientations, + mut velocities, + 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; + let _ = force_updates.insert(b, ForceUpdate); + } + } + 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 new file mode 100644 index 0000000000..5bd45ac7c7 --- /dev/null +++ b/common/src/sys/controller.rs @@ -0,0 +1,117 @@ +use crate::{ + comp::{ + Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, MoveDir, + OnGround, Respawning, Rolling, Stats, {ForceUpdate, Ori, Pos, Vel}, + }, + state::DeltaTime, +}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; + +/// This system is responsible for validating controller inputs +pub struct Sys; +impl<'a> System<'a> for Sys { + type SystemData = ( + Entities<'a>, + Read<'a, DeltaTime>, + ReadStorage<'a, Controller>, + ReadStorage<'a, Stats>, + ReadStorage<'a, Pos>, + ReadStorage<'a, Vel>, + ReadStorage<'a, Ori>, + ReadStorage<'a, OnGround>, + WriteStorage<'a, MoveDir>, + WriteStorage<'a, Jumping>, + WriteStorage<'a, Attacking>, + WriteStorage<'a, Rolling>, + WriteStorage<'a, Respawning>, + WriteStorage<'a, Gliding>, + ); + + fn run( + &mut self, + ( + entities, + dt, + controllers, + stats, + positions, + velocities, + orientations, + on_grounds, + mut move_dirs, + mut jumpings, + mut attackings, + mut rollings, + mut respawns, + mut glidings, + ): Self::SystemData, + ) { + for (entity, controller, stats, pos, vel, ori, on_ground) in ( + &entities, + &controllers, + &stats, + &positions, + &velocities, + &orientations, + on_grounds.maybe(), + ) + .join() + { + if stats.is_dead { + // Respawn + if controller.respawn { + respawns.insert(entity, Respawning); + } + continue; + } + + // Move dir + if rollings.get(entity).is_none() { + move_dirs.insert( + entity, + MoveDir(if controller.move_dir.magnitude() > 1.0 { + controller.move_dir.normalized() + } else { + controller.move_dir + }), + ); + } + + // Glide + if controller.glide + && on_ground.is_none() + && attackings.get(entity).is_none() + && rollings.get(entity).is_none() + { + glidings.insert(entity, Gliding); + } else { + glidings.remove(entity); + } + + // Attack + if controller.attack + && attackings.get(entity).is_none() + && glidings.get(entity).is_none() + && rollings.get(entity).is_none() + { + attackings.insert(entity, Attacking::start()); + } + + // Jump + if controller.jump && on_ground.is_some() && vel.0.z <= 0.0 { + jumpings.insert(entity, Jumping); + } + + // Roll + if controller.roll + && rollings.get(entity).is_none() + && attackings.get(entity).is_none() + && glidings.get(entity).is_none() + && on_ground.is_some() + && vel.0.magnitude() > 5.0 + { + rollings.insert(entity, Rolling::start()); + } + } + } +} diff --git a/common/src/sys/inputs.rs b/common/src/sys/inputs.rs deleted file mode 100644 index 288a0e0ca0..0000000000 --- a/common/src/sys/inputs.rs +++ /dev/null @@ -1,174 +0,0 @@ -use crate::{ - comp::{ - phys::{ForceUpdate, Ori, Pos, Vel}, - Animation, AnimationInfo, Attacking, Control, Gliding, HealthSource, Jumping, Stats, - }, - state::{DeltaTime, Uid}, - terrain::TerrainMap, - vol::{ReadVol, Vox}, -}; -use log::warn; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; -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>, - ReadStorage<'a, Uid>, - Read<'a, DeltaTime>, - ReadExpect<'a, TerrainMap>, - ReadStorage<'a, Pos>, - WriteStorage<'a, Vel>, - WriteStorage<'a, Ori>, - WriteStorage<'a, AnimationInfo>, - WriteStorage<'a, Stats>, - ReadStorage<'a, Control>, - WriteStorage<'a, Jumping>, - WriteStorage<'a, Gliding>, - WriteStorage<'a, Attacking>, - WriteStorage<'a, ForceUpdate>, - ); - - fn run( - &mut self, - ( - entities, - uids, - dt, - terrain, - positions, - mut velocities, - mut orientations, - mut animation_infos, - mut stats, - controls, - mut jumps, - glides, - mut attacks, - mut force_updates, - ): Self::SystemData, - ) { - for (entity, pos, control, stats, mut ori, mut vel) in ( - &entities, - &positions, - &controls, - &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 { - Animation::Run - } else if attacks.get(entity).is_some() { - Animation::Attack - } else { - Animation::Idle - } - } else if glides.get(entity).is_some() { - Animation::Gliding - } else { - Animation::Jump - }; - - let last = animation_infos - .get_mut(entity) - .cloned() - .unwrap_or(AnimationInfo::default()); - let changed = last.animation != animation; - - if let Err(err) = animation_infos.insert( - entity, - AnimationInfo { - animation, - time: if changed { 0.0 } else { last.time }, - changed, - }, - ) { - warn!("Inserting AnimationInfo for an entity failed: {:?}", err); - } - } - - for (entity, &uid, pos, ori, attacking) in - (&entities, &uids, &positions, &orientations, &mut attacks).join() - { - if !attacking.applied { - 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; - } - } - } -} diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index da6f62958b..65b24f12fa 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -1,7 +1,7 @@ -pub mod actions; pub mod agent; pub mod animation; -pub mod inputs; +pub mod combat; +pub mod controller; pub mod phys; mod stats; @@ -10,17 +10,17 @@ use specs::DispatcherBuilder; // System names const AGENT_SYS: &str = "agent_sys"; -const INPUTS_SYS: &str = "inputs_sys"; -const ACTIONS_SYS: &str = "actions_sys"; +const CONTROLLER_SYS: &str = "controller_sys"; const PHYS_SYS: &str = "phys_sys"; +const COMBAT_SYS: &str = "combat_sys"; const ANIMATION_SYS: &str = "animation_sys"; const STATS_SYS: &str = "stats_sys"; 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(actions::Sys, ACTIONS_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 796b3310e2..44dfdeecd3 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,21 +1,26 @@ use crate::{ - comp::{ - phys::{Pos, Vel}, - Stats, - }, + comp::{Gliding, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Stats, Vel}, state::DeltaTime, terrain::TerrainMap, vol::{ReadVol, Vox}, }; -use specs::{Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; use vek::*; -// Basic ECS physics system -pub struct Sys; - const GRAVITY: f32 = 9.81 * 4.0; const FRIC_GROUND: f32 = 0.15; const FRIC_AIR: f32 = 0.015; +const HUMANOID_ACCEL: f32 = 70.0; +const HUMANOID_SPEED: f32 = 120.0; +const HUMANOID_AIR_ACCEL: f32 = 10.0; +const HUMANOID_AIR_SPEED: f32 = 100.0; +const HUMANOID_JUMP_ACCEL: f32 = 16.0; +const ROLL_ACCEL: f32 = 160.0; +const ROLL_SPEED: f32 = 550.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 @@ -38,34 +43,128 @@ fn integrate_forces(dt: f32, mut lv: Vec3, damp: f32) -> Vec3 { lv } +/// 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, MoveDir>, + ReadStorage<'a, Gliding>, ReadStorage<'a, Stats>, + WriteStorage<'a, Jumping>, + WriteStorage<'a, Rolling>, + WriteStorage<'a, OnGround>, WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, + WriteStorage<'a, Ori>, ); - fn run(&mut self, (terrain, dt, stats, mut positions, mut velocities): Self::SystemData) { - for (stats, pos, vel) in (&stats, &mut positions, &mut velocities).join() { - // Disable while dead TODO: Replace with client states + fn run( + &mut self, + ( + entities, + terrain, + dt, + move_dirs, + glidings, + stats, + mut jumpings, + mut rollings, + mut on_grounds, + mut positions, + mut velocities, + mut orientations, + ): Self::SystemData, + ) { + // Apply movement inputs + for (entity, stats, move_dir, gliding, mut pos, mut vel, mut ori) in ( + &entities, + &stats, + move_dirs.maybe(), + glidings.maybe(), + &mut positions, + &mut velocities, + &mut orientations, + ) + .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; + // Move player according to move_dir + if let Some(move_dir) = move_dir { + vel.0 += Vec2::broadcast(dt.0) + * move_dir.0 + * match ( + on_grounds.get(entity).is_some(), + glidings.get(entity).is_some(), + rollings.get(entity).is_some(), + ) { + (true, false, false) if vel.0.magnitude() < HUMANOID_SPEED => { + HUMANOID_ACCEL + } + (false, true, false) if vel.0.magnitude() < GLIDE_SPEED => GLIDE_ACCEL, + (false, false, false) if vel.0.magnitude() < HUMANOID_AIR_SPEED => { + HUMANOID_AIR_ACCEL + } + (true, false, true) if vel.0.magnitude() < ROLL_SPEED => ROLL_ACCEL, + + _ => 0.0, + }; + } + + // Jump + if jumpings.get(entity).is_some() { + vel.0.z = HUMANOID_JUMP_ACCEL; + jumpings.remove(entity); + } + + // 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); + } + + // Roll + if let Some(time) = rollings.get_mut(entity).map(|r| &mut r.time) { + *time += dt.0; + if *time > 0.55 { + rollings.remove(entity); + } + } + + // 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; + // Update OnGround component + if 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 + { + on_grounds.insert(entity, OnGround); + } else { + on_grounds.remove(entity); + } + // Integrate forces // Friction is assumed to be a constant dependent on location - let friction = 50.0 * if on_ground { FRIC_GROUND } else { FRIC_AIR }; + let friction = 50.0 + * if on_grounds.get(entity).is_some() { + FRIC_GROUND + } else { + FRIC_AIR + }; vel.0 = integrate_forces(dt.0, vel.0, friction); // Basic collision with terrain diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index 3897da618f..22dc10d48b 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -5,9 +5,8 @@ use crate::{ use log::warn; use specs::{Entities, Join, Read, System, WriteStorage}; -// Basic ECS AI agent system +/// This system kills players pub struct Sys; - impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index ba8480420d..510a171ff9 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -100,18 +100,12 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32); match (opt_x, opt_y, opt_z) { (Some(x), Some(y), Some(z)) => { - match server - .state - .read_component_cloned::(entity) - { + match server.state.read_component_cloned::(entity) { Some(current_pos) => { - server.state.write_component( - entity, - comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z)), - ); server .state - .write_component(entity, comp::phys::ForceUpdate); + .write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); + server.state.write_component(entity, comp::ForceUpdate); } None => server.clients.notify( entity, @@ -131,10 +125,8 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch (Some(x), Some(y), Some(z)) => { server .state - .write_component(entity, comp::phys::Pos(Vec3::new(x, y, z))); - server - .state - .write_component(entity, comp::phys::ForceUpdate); + .write_component(entity, comp::Pos(Vec3::new(x, y, z))); + server.state.write_component(entity, comp::ForceUpdate); } _ => server .clients @@ -173,20 +165,15 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat match opt_alias { Some(alias) => { let ecs = server.state.ecs(); - let opt_player = (&ecs.entities(), &ecs.read_storage::()) + let opt_player = (&ecs.entities(), &ecs.read_storage::()) .join() .find(|(_, player)| player.alias == alias) .map(|(entity, _)| entity); match opt_player { - Some(player) => match server - .state - .read_component_cloned::(player) - { + Some(player) => match server.state.read_component_cloned::(player) { Some(pos) => { server.state.write_component(entity, pos); - server - .state - .write_component(entity, comp::phys::ForceUpdate); + server.state.write_component(entity, comp::ForceUpdate); } None => server.clients.notify( entity, @@ -222,10 +209,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C match (opt_agent, opt_id, opt_amount) { (Some(agent), Some(id), Some(amount)) => { - match server - .state - .read_component_cloned::(entity) - { + match server.state.read_component_cloned::(entity) { Some(mut pos) => { pos.0.x += 1.0; // Temp fix TODO: Solve NaN issue with positions of pets let body = kind_to_body(id); diff --git a/server/src/lib.rs b/server/src/lib.rs index ead806c46c..79a382feae 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -135,7 +135,7 @@ impl Server { #[allow(dead_code)] pub fn create_npc( &mut self, - pos: comp::phys::Pos, + pos: comp::Pos, name: String, body: comp::Body, ) -> EcsEntityBuilder { @@ -143,13 +143,13 @@ impl Server { .ecs_mut() .create_entity_synced() .with(pos) - .with(comp::phys::Vel(Vec3::zero())) - .with(comp::phys::Ori(Vec3::unit_y())) - .with(comp::Control::default()) + .with(comp::Vel(Vec3::zero())) + .with(comp::Ori(Vec3::unit_y())) + .with(comp::Controller::default()) .with(comp::AnimationInfo::default()) .with(comp::Actor::Character { name, body }) .with(comp::Stats::default()) - .with(comp::phys::ForceUpdate) + .with(comp::ForceUpdate) } pub fn create_player_character( @@ -164,11 +164,12 @@ 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::phys::Pos(spawn_point)); - state.write_component(entity, comp::phys::Vel(Vec3::zero())); - state.write_component(entity, comp::phys::Ori(Vec3::unit_y())); + state.write_component(entity, comp::Controller::default()); + state.write_component(entity, comp::Pos(spawn_point)); + state.write_component(entity, comp::Vel(Vec3::zero())); + state.write_component(entity, comp::Ori(Vec3::unit_y())); // Make sure physics are accepted. - state.write_component(entity, comp::phys::ForceUpdate); + state.write_component(entity, comp::ForceUpdate); // Tell the client its request was successful. client.allow_state(ClientState::Character); @@ -245,9 +246,8 @@ impl Server { // Actually kill them for entity in todo_kill { if let Some(client) = self.clients.get_mut(&entity) { - self.state - .write_component(entity, comp::phys::Vel(Vec3::zero())); - self.state.write_component(entity, comp::phys::ForceUpdate); + self.state.write_component(entity, comp::Vel(Vec3::zero())); + self.state.write_component(entity, comp::ForceUpdate); client.force_state(ClientState::Dead); } else { if let Err(err) = self.state.ecs_mut().delete_entity_synced(entity) { @@ -272,12 +272,11 @@ impl Server { self.state.write_component(entity, comp::Stats::default()); self.state .ecs_mut() - .write_storage::() + .write_storage::() .get_mut(entity) .map(|pos| pos.0.z += 100.0); - self.state - .write_component(entity, comp::phys::Vel(Vec3::zero())); - self.state.write_component(entity, comp::phys::ForceUpdate); + self.state.write_component(entity, comp::Vel(Vec3::zero())); + self.state.write_component(entity, comp::ForceUpdate); } } @@ -288,7 +287,7 @@ impl Server { for (entity, view_distance, pos) in ( &self.state.ecs().entities(), &self.state.ecs().read_storage::(), - &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), ) .join() .filter_map(|(entity, player, pos)| { @@ -323,7 +322,7 @@ impl Server { // For each player with a position, calculate the distance. for (player, pos) in ( &self.state.ecs().read_storage::(), - &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), ) .join() { @@ -492,24 +491,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 => { @@ -521,15 +512,6 @@ impl Server { | ClientState::Character => new_chat_msgs.push((Some(entity), msg)), ClientState::Pending => {} }, - ClientMsg::PlayerAnimation(animation_info) => { - match client.client_state { - ClientState::Character => { - state.write_component(entity, animation_info) - } - // Only characters can send animations. - _ => client.error_state(RequestStateError::Impossible), - } - } ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state { ClientState::Character => { state.write_component(entity, pos); @@ -639,9 +621,9 @@ impl Server { // Sync physics for (&uid, &pos, &vel, &ori) in ( &state.ecs().read_storage::(), - &state.ecs().read_storage::(), - &state.ecs().read_storage::(), - &state.ecs().read_storage::(), + &state.ecs().read_storage::(), + &state.ecs().read_storage::(), + &state.ecs().read_storage::(), ) .join() { @@ -680,13 +662,10 @@ impl Server { for (entity, &uid, &pos, &vel, &ori, force_update) in ( &self.state.ecs().entities(), &self.state.ecs().read_storage::(), - &self.state.ecs().read_storage::(), - &self.state.ecs().read_storage::(), - &self.state.ecs().read_storage::(), - self.state - .ecs() - .read_storage::() - .maybe(), + &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), + self.state.ecs().read_storage::().maybe(), ) .join() { @@ -702,7 +681,7 @@ impl Server { let in_vd = |entity| { // Get client position. - let client_pos = match state.ecs().read_storage::().get(entity) { + let client_pos = match state.ecs().read_storage::().get(entity) { Some(pos) => pos.0, None => return false, }; @@ -733,10 +712,7 @@ impl Server { &self.state.ecs().entities(), &self.state.ecs().read_storage::(), &self.state.ecs().read_storage::(), - self.state - .ecs() - .read_storage::() - .maybe(), + self.state.ecs().read_storage::().maybe(), ) .join() { @@ -755,7 +731,7 @@ impl Server { // Remove all force flags. self.state .ecs_mut() - .write_storage::() + .write_storage::() .clear(); } diff --git a/voxygen/src/anim/character/attack.rs b/voxygen/src/anim/character/attack.rs index ab8e7bf109..4676411c0c 100644 --- a/voxygen/src/anim/character/attack.rs +++ b/voxygen/src/anim/character/attack.rs @@ -46,11 +46,11 @@ impl Animation for AttackAnimation { -8.0 + wave_quicken_slow * 10.0, 4.0 + wave_quicken_double * 3.0, 9.0, - ) / 11.0; + ); next.l_hand.ori = Quaternion::rotation_z(-0.8) * Quaternion::rotation_x(0.0 + wave_quicken * -0.8) * Quaternion::rotation_y(0.0 + wave_quicken * -0.4); - next.l_hand.scale = Vec3::one() / 11.0; + next.l_hand.scale = Vec3::one(); next.r_hand.offset = Vec3::new(0.0, -2.0, 6.5) / 11.0; next.r_hand.ori = Quaternion::rotation_x(0.0); diff --git a/voxygen/src/anim/character/cidle.rs b/voxygen/src/anim/character/cidle.rs new file mode 100644 index 0000000000..7baff85c1e --- /dev/null +++ b/voxygen/src/anim/character/cidle.rs @@ -0,0 +1,104 @@ +use super::{super::Animation, CharacterSkeleton}; +use std::{f32::consts::PI, ops::Mul}; +use vek::*; + +pub struct CidleAnimation; + +impl Animation for CidleAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = f64; + + fn update_skeleton( + skeleton: &Self::Skeleton, + global_time: f64, + anim_time: f64, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin(); + let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI).cos(); + + let head_look = Vec2::new( + ((global_time + anim_time) as f32 / 8.0) + .floor() + .mul(7331.0) + .sin() + * 0.5, + ((global_time + anim_time) as f32 / 8.0) + .floor() + .mul(1337.0) + .sin() + * 0.25, + ); + next.head.offset = Vec3::new(0.0, 2.0, 11.0 + wave_ultra_slow * 0.3); + next.head.ori = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y); + next.head.scale = Vec3::one(); + + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_ultra_slow * 0.3); + next.chest.ori = Quaternion::rotation_x(0.0); + next.chest.scale = Vec3::one(); + + next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_ultra_slow * 0.3); + next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.scale = Vec3::one(); + + next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_ultra_slow * 0.3); + next.shorts.ori = Quaternion::rotation_x(0.0); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new( + -7.5, + -2.0 + wave_ultra_slow_cos * 0.15, + 8.0 + wave_ultra_slow * 0.5, + ) / 11.0; + + next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06); + next.l_hand.scale = Vec3::one() / 11.0; + + next.r_hand.offset = Vec3::new( + 7.5, + -2.0 + wave_ultra_slow_cos * 0.15, + 8.0 + wave_ultra_slow * 0.5, + ) / 11.0; + next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06); + next.r_hand.scale = Vec3::one() / 11.; + + next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); + next.l_foot.ori = Quaternion::identity(); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, -0.1, 8.0); + next.r_foot.ori = Quaternion::identity(); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new(-7.0, -5.0, 15.0); + next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.weapon.scale = Vec3::one() * 0.0; + + next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5); + next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.scale = Vec3::one() * 1.04; + + next.r_shoulder.offset = Vec3::new(0.0, -3.2, 2.5); + next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.scale = Vec3::one() * 1.04; + + next.draw.offset = Vec3::new(0.0, 5.0, 0.0); + next.draw.ori = Quaternion::rotation_y(0.0); + next.draw.scale = Vec3::one() * 0.0; + + next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.left_equip.ori = Quaternion::rotation_x(0.0);; + next.left_equip.scale = Vec3::one() * 0.0; + + next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.right_equip.ori = Quaternion::rotation_x(0.0);; + next.right_equip.scale = Vec3::one() * 0.0; + + next.torso.offset = Vec3::new(0.0, -0.2, 0.1); + next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.scale = Vec3::one() / 11.0; + + next + } +} diff --git a/voxygen/src/anim/character/crun.rs b/voxygen/src/anim/character/crun.rs new file mode 100644 index 0000000000..7b1adb5047 --- /dev/null +++ b/voxygen/src/anim/character/crun.rs @@ -0,0 +1,97 @@ +use super::{super::Animation, CharacterSkeleton}; +use std::ops::Mul; +use vek::*; + +pub struct CrunAnimation; + +impl Animation for CrunAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = (f32, f64); + + fn update_skeleton( + skeleton: &Self::Skeleton, + (velocity, global_time): Self::Dependency, + anim_time: f64, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave = (anim_time as f32 * 14.0).sin(); + let wave_cos = (anim_time as f32 * 14.0).cos(); + + let head_look = Vec2::new( + ((global_time + anim_time) as f32 / 2.0) + .floor() + .mul(7331.0) + .sin() + * 0.2, + ((global_time + anim_time) as f32 / 2.0) + .floor() + .mul(1337.0) + .sin() + * 0.1, + ); + + next.head.offset = Vec3::new(0.0, 3.0, 12.0 + wave_cos * 1.3); + next.head.ori = Quaternion::rotation_z(head_look.x + wave * 0.1) + * Quaternion::rotation_x(head_look.y + 0.35); + next.head.scale = Vec3::one(); + + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_cos * 1.1); + next.chest.ori = Quaternion::rotation_z(wave * 0.1); + next.chest.scale = Vec3::one(); + + next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_cos * 1.1); + next.belt.ori = Quaternion::rotation_z(wave * 0.25); + next.belt.scale = Vec3::one(); + + next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_cos * 1.1); + next.shorts.ori = Quaternion::rotation_z(wave * 0.6); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new(-9.0, 3.0 + wave_cos * 8.0, 12.0 - wave * 1.0) / 11.0; + next.l_hand.ori = Quaternion::rotation_x(wave_cos * 1.1); + next.l_hand.scale = Vec3::one() / 11.0; + + next.r_hand.offset = Vec3::new(9.0, 3.0 - wave_cos * 8.0, 12.0 + wave * 1.0) / 11.0; + next.r_hand.ori = Quaternion::rotation_x(wave_cos * -1.1); + next.r_hand.scale = Vec3::one() / 11.0; + + next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0); + next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0); + next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new(-7.0, -5.0, 15.0); + next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.weapon.scale = Vec3::one() * 0.0; + + next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5); + next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.scale = Vec3::one() * 1.04; + + next.r_shoulder.offset = Vec3::new(0.0, -3.2, 2.5); + next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.scale = Vec3::one() * 1.04; + + next.draw.offset = Vec3::new(0.0, 5.0, 0.0); + next.draw.ori = Quaternion::rotation_y(0.0); + next.draw.scale = Vec3::one() * 0.0; + + next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.left_equip.ori = Quaternion::rotation_x(0.0);; + next.left_equip.scale = Vec3::one() * 0.0; + + next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.right_equip.ori = Quaternion::rotation_x(0.0);; + next.right_equip.scale = Vec3::one() * 0.0; + + next.torso.offset = Vec3::new(0.0, -0.2, 0.4); + next.torso.ori = Quaternion::rotation_x(-velocity * 0.04 - wave_cos * 0.10); + next.torso.scale = Vec3::one() / 11.0; + + next + } +} diff --git a/voxygen/src/anim/character/gliding.rs b/voxygen/src/anim/character/gliding.rs index 8c21326638..82edf35f15 100644 --- a/voxygen/src/anim/character/gliding.rs +++ b/voxygen/src/anim/character/gliding.rs @@ -6,11 +6,11 @@ pub struct GlidingAnimation; impl Animation for GlidingAnimation { type Skeleton = CharacterSkeleton; - type Dependency = f64; + type Dependency = (f32, f64); fn update_skeleton( skeleton: &Self::Skeleton, - global_time: f64, + (velocity, global_time): Self::Dependency, anim_time: f64, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); @@ -35,48 +35,40 @@ impl Animation for GlidingAnimation { .sin() * 0.25, ); - next.head.offset = Vec3::new(0.0, 2.0, 12.0); + next.head.offset = Vec3::new(0.0, 2.0, 2.0); next.head.ori = Quaternion::rotation_x(0.35 - wave_very_slow * 0.10 + head_look.y) * Quaternion::rotation_z(head_look.x + wave_very_slow_cos * 0.15); next.head.scale = Vec3::one(); - next.chest.offset = Vec3::new(0.0, 0.0, 8.0); + next.chest.offset = Vec3::new(0.0, 0.0, -2.0); next.chest.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.15); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, 6.0); + next.belt.offset = Vec3::new(0.0, 0.0, -4.0); next.belt.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.20); next.belt.scale = Vec3::one(); - next.shorts.offset = Vec3::new(0.0, 0.0, 3.0); + next.shorts.offset = Vec3::new(0.0, 0.0, -7.0); next.shorts.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.25); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( - -10.0, - 6.0 - wave_very_slow * 1.50, - 15.0 + wave_very_slow * 0.50, - ) / 11.0; - next.l_hand.ori = Quaternion::rotation_x(0.2 + wave_very_slow_cos * 0.05); - next.l_hand.scale = Vec3::one() / 11.0; + next.l_hand.offset = Vec3::new(-10.0, -2.0 + wave_very_slow * 0.10, 10.5); + next.l_hand.ori = Quaternion::rotation_x(1.0 + wave_very_slow_cos * -0.10); + next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( - 10.0, - 6.0 - wave_very_slow * 1.50, - 14.5 + wave_very_slow * 0.50, - ) / 11.0; - next.r_hand.ori = Quaternion::rotation_x(0.1 + wave_very_slow * 0.05); - next.r_hand.scale = Vec3::one() / 11.0; + next.r_hand.offset = Vec3::new(10.0, -2.0 + wave_very_slow * 0.10, 10.5); + next.r_hand.ori = Quaternion::rotation_x(1.0 + wave_very_slow_cos * -0.10); + next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-3.4, 1.0, 8.0); + next.l_foot.offset = Vec3::new(-3.4, 1.0, -2.0); next.l_foot.ori = Quaternion::rotation_x( - wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19, + (wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19) * velocity * 0.07, ); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(3.4, 1.0, 8.0); + next.r_foot.offset = Vec3::new(3.4, 1.0, -2.0); next.r_foot.ori = Quaternion::rotation_x( - wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13, + (wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13) * velocity * 0.07, ); next.r_foot.scale = Vec3::one(); @@ -92,21 +84,21 @@ impl Animation for GlidingAnimation { next.r_shoulder.ori = Quaternion::rotation_x(0.0); next.r_shoulder.scale = Vec3::one() * 1.04; - next.draw.offset = Vec3::new(0.0, -9.0 + wave_very_slow * 0.10, 18.0); - next.draw.ori = Quaternion::rotation_x(0.95 - wave_very_slow * 0.15) + next.draw.offset = Vec3::new(0.0, -9.0 + wave_very_slow * 0.10, 8.0); + next.draw.ori = Quaternion::rotation_x(1.0)//0.95 - wave_very_slow * 0.08) * Quaternion::rotation_y(wave_very_slow_cos * 0.04); next.draw.scale = Vec3::one(); - next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.left_equip.offset = Vec3::new(0.0, 0.0, -5.0) / 11.0; next.left_equip.ori = Quaternion::rotation_x(0.0);; next.left_equip.scale = Vec3::one() * 0.0; - next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.right_equip.offset = Vec3::new(0.0, 0.0, -5.0) / 11.0; next.right_equip.ori = Quaternion::rotation_x(0.0);; next.right_equip.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.2, 0.0); - next.torso.ori = Quaternion::rotation_x(-0.8 + wave_very_slow * 0.10); + next.torso.offset = Vec3::new(0.0, -0.2, 10.0) / 11.0; + next.torso.ori = Quaternion::rotation_x(-0.05 * velocity + wave_very_slow * 0.10); next.torso.scale = Vec3::one() / 11.0; next diff --git a/voxygen/src/anim/character/idle.rs b/voxygen/src/anim/character/idle.rs index 9203c8b99d..8b68ff656b 100644 --- a/voxygen/src/anim/character/idle.rs +++ b/voxygen/src/anim/character/idle.rs @@ -51,20 +51,20 @@ impl Animation for IdleAnimation { next.l_hand.offset = Vec3::new( -7.5, - -2.0 + wave_ultra_slow_cos * 0.15, - 8.0 + wave_ultra_slow * 0.5, - ) / 11.0; + 0.0 + wave_ultra_slow_cos * 0.15, + 7.0 + wave_ultra_slow * 0.5, + ); - next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06); - next.l_hand.scale = Vec3::one() / 11.0; + next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.l_hand.scale = Vec3::one(); next.r_hand.offset = Vec3::new( 7.5, - -2.0 + wave_ultra_slow_cos * 0.15, - 8.0 + wave_ultra_slow * 0.5, - ) / 11.0; - next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06); - next.r_hand.scale = Vec3::one() / 11.; + 0.0 + wave_ultra_slow_cos * 0.15, + 7.0 + wave_ultra_slow * 0.5, + ); + next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.r_hand.scale = Vec3::one(); next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); next.l_foot.ori = Quaternion::identity(); diff --git a/voxygen/src/anim/character/jump.rs b/voxygen/src/anim/character/jump.rs index 6a1e7d95fc..d3487b388f 100644 --- a/voxygen/src/anim/character/jump.rs +++ b/voxygen/src/anim/character/jump.rs @@ -39,17 +39,17 @@ impl Animation for JumpAnimation { -8.0, 0.0 + wave_stop * 3.8, 7.0 + wave_stop * 3.2 - wave * 0.4, - ) / 11.0; + ); next.l_hand.ori = Quaternion::rotation_x(wave_stop_alt * 0.6); - next.l_hand.scale = Vec3::one() / 11.0; + next.l_hand.scale = Vec3::one(); next.r_hand.offset = Vec3::new( 8.0, 0.0 + wave_stop * -3.8, 7.0 + wave_stop * 3.2 - wave * 0.4, - ) / 11.0; + ); next.r_hand.ori = Quaternion::rotation_x(-wave_stop_alt * 0.6); - next.r_hand.scale = Vec3::one() / 11.0; + next.r_hand.scale = Vec3::one(); next.l_foot.offset = Vec3::new(-3.4, 1.0, 6.0); next.l_foot.ori = Quaternion::rotation_x(wave_stop * -1.2 - wave_slow * 0.2); diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index 8311183815..e570b7cf0a 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -1,14 +1,20 @@ pub mod attack; +pub mod cidle; +pub mod crun; pub mod gliding; pub mod idle; pub mod jump; +pub mod roll; pub mod run; // Reexports pub use self::attack::AttackAnimation; +pub use self::cidle::CidleAnimation; +pub use self::crun::CrunAnimation; pub use self::gliding::GlidingAnimation; pub use self::idle::IdleAnimation; pub use self::jump::JumpAnimation; +pub use self::roll::RollAnimation; pub use self::run::RunAnimation; use super::{Bone, Skeleton}; @@ -68,8 +74,8 @@ impl Skeleton for CharacterSkeleton { FigureBoneData::new(torso_mat * chest_mat), FigureBoneData::new(torso_mat * self.belt.compute_base_matrix()), FigureBoneData::new(torso_mat * self.shorts.compute_base_matrix()), - FigureBoneData::new(l_hand_mat), - FigureBoneData::new(self.r_hand.compute_base_matrix()), + FigureBoneData::new(torso_mat * l_hand_mat), + FigureBoneData::new(torso_mat * self.r_hand.compute_base_matrix()), FigureBoneData::new(torso_mat * self.l_foot.compute_base_matrix()), FigureBoneData::new(torso_mat * self.r_foot.compute_base_matrix()), FigureBoneData::new(torso_mat * chest_mat * weapon_mat), diff --git a/voxygen/src/anim/character/roll.rs b/voxygen/src/anim/character/roll.rs new file mode 100644 index 0000000000..f5fa454ab8 --- /dev/null +++ b/voxygen/src/anim/character/roll.rs @@ -0,0 +1,98 @@ +use super::{super::Animation, CharacterSkeleton}; +use std::{f32::consts::PI, ops::Mul}; +use vek::*; + +pub struct RollAnimation; + +impl Animation for RollAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = f64; + + fn update_skeleton( + skeleton: &Self::Skeleton, + global_time: f64, + anim_time: f64, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave = (anim_time as f32 * 5.5).sin(); + let wave_quick = (anim_time as f32 * 9.5).sin(); + let wave_quick_cos = (anim_time as f32 * 9.5).cos(); + let wave_cos = (anim_time as f32 * 5.5).cos(); + let wave_slow = (anim_time as f32 * 2.8 + PI).sin(); + let wave_dub = (anim_time as f32 * 5.5).sin(); + + next.head.offset = Vec3::new(0.0, 0.0 + wave_slow * -3.0, 9.0 + wave_dub * -5.0); + next.head.ori = Quaternion::rotation_x(wave_dub * -0.4); + next.head.scale = Vec3::one(); + + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_dub * -2.5); + next.chest.ori = Quaternion::rotation_x(wave_dub * -0.5); + next.chest.scale = Vec3::one() * 1.01; + + next.belt.offset = Vec3::new(0.0, 0.0, 5.0); + next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.scale = Vec3::one(); + + next.shorts.offset = Vec3::new(0.0, 0.0, 2.0); + next.shorts.ori = Quaternion::rotation_x(0.0); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new( + -5.5 + wave * -0.5, + -2.0 + wave_quick_cos * -5.5, + 8.0 + wave_quick * 0.5, + ); + + next.l_hand.ori = + Quaternion::rotation_x(wave_slow * 6.5) * Quaternion::rotation_y(wave * 0.3); + next.l_hand.scale = Vec3::one(); + + next.r_hand.offset = Vec3::new( + 5.5 + wave * 0.5, + -2.0 + wave_quick_cos * 2.5, + 8.0 + wave_quick * 3.0, + ); + next.r_hand.ori = + Quaternion::rotation_x(wave_slow * 6.5) * Quaternion::rotation_y(wave * 0.3); + next.r_hand.scale = Vec3::one(); + + next.l_foot.offset = Vec3::new(-3.4, -0.1, 9.0 - 0.0 + wave_dub * -1.2 + wave_slow * 4.0); + next.l_foot.ori = Quaternion::rotation_x(wave * 0.6); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, -0.1, 9.0 - 0.0 + wave_dub * -1.0 + wave_slow * 4.0); + next.r_foot.ori = Quaternion::rotation_x(wave * -0.4); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new(-7.0, -7.0, 15.0); + next.weapon.ori = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_quick * 1.0); + next.weapon.scale = Vec3::one(); + + next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5); + next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.scale = Vec3::one() * 1.04; + + next.r_shoulder.offset = Vec3::new(0.0, -3.2, 2.5); + next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.scale = Vec3::one() * 1.04; + + next.draw.offset = Vec3::new(0.0, 5.0, 0.0); + next.draw.ori = Quaternion::rotation_y(0.0); + next.draw.scale = Vec3::one() * 0.0; + + next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.left_equip.ori = Quaternion::rotation_x(0.0);; + next.left_equip.scale = Vec3::one() * 0.0; + + next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0; + next.right_equip.ori = Quaternion::rotation_x(0.0);; + next.right_equip.scale = Vec3::one() * 0.0; + + next.torso.offset = Vec3::new(0.0, -2.2, 0.1 + wave_dub * 16.0) / 11.0; + next.torso.ori = Quaternion::rotation_x(wave_slow * 6.0); + next.torso.scale = Vec3::one() / 11.0; + next + } +} diff --git a/voxygen/src/anim/character/run.rs b/voxygen/src/anim/character/run.rs index 0c188fde45..0e2cf4474f 100644 --- a/voxygen/src/anim/character/run.rs +++ b/voxygen/src/anim/character/run.rs @@ -1,4 +1,5 @@ use super::{super::Animation, CharacterSkeleton}; +use std::f32::consts::PI; use std::ops::Mul; use vek::*; @@ -15,8 +16,11 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave = (anim_time as f32 * 14.0).sin(); - let wave_cos = (anim_time as f32 * 14.0).cos(); + let wave = (anim_time as f32 * 12.0).sin(); + let wave_cos = (anim_time as f32 * 12.0).cos(); + let wave_diff = (anim_time as f32 * 12.0 + PI / 2.0).sin(); + let wave_cos_dub = (anim_time as f32 * 24.0).cos(); + let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0).sin(); let head_look = Vec2::new( ((global_time + anim_time) as f32 / 2.0) @@ -48,24 +52,33 @@ impl Animation for RunAnimation { next.shorts.ori = Quaternion::rotation_z(wave * 0.6); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(-9.0, 3.0 + wave_cos * 8.0, 12.0 - wave * 1.0) / 11.0; - next.l_hand.ori = Quaternion::rotation_x(wave_cos * 1.1); - next.l_hand.scale = Vec3::one() / 11.0; + next.l_hand.offset = Vec3::new( + -7.5 + wave_cos_dub * 1.0, + 2.0 + wave_cos * 5.0, + 7.0 - wave * 1.5, + ); + next.l_hand.ori = Quaternion::rotation_x(wave_cos * 0.8); + next.l_hand.scale = Vec3::one() * 1.0; - next.r_hand.offset = Vec3::new(9.0, 3.0 - wave_cos * 8.0, 12.0 + wave * 1.0) / 11.0; - next.r_hand.ori = Quaternion::rotation_x(wave_cos * -1.1); - next.r_hand.scale = Vec3::one() / 11.0; + next.r_hand.offset = Vec3::new( + 7.5 - wave_cos_dub * 1.0, + 2.0 - wave_cos * 5.0, + 7.0 + wave * 1.5, + ); + next.r_hand.ori = Quaternion::rotation_x(wave_cos * -0.8); + next.r_hand.scale = Vec3::one() * 1.0; - next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0); - next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5); + next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 7.0); + next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.3); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0); - next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5); + next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 7.0); + next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.3); next.r_foot.scale = Vec3::one(); next.weapon.offset = Vec3::new(-7.0, -5.0, 15.0); - next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.weapon.ori = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); next.weapon.scale = Vec3::one(); next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5); @@ -89,7 +102,8 @@ impl Animation for RunAnimation { next.right_equip.scale = Vec3::one() * 0.0; next.torso.offset = Vec3::new(0.0, -0.2, 0.4); - next.torso.ori = Quaternion::rotation_x(-velocity * 0.04 - wave_cos * 0.10); + next.torso.ori = + Quaternion::rotation_x(wave_stop * velocity * -0.06 + wave_diff * velocity * -0.005); next.torso.scale = Vec3::one() / 11.0; next diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index ccafa92385..34ecf90771 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -103,7 +103,7 @@ font_ids! { pub struct DebugInfo { pub tps: f64, pub ping_ms: f64, - pub coordinates: Option, + pub coordinates: Option, } pub enum Event { @@ -312,7 +312,7 @@ impl Hud { if self.show.ingame { let ecs = client.state().ecs(); let actor = ecs.read_storage::(); - let pos = ecs.read_storage::(); + let pos = ecs.read_storage::(); let stats = ecs.read_storage::(); let player = ecs.read_storage::(); let entities = ecs.entities(); @@ -322,7 +322,7 @@ impl Hud { let player_pos = client .state() .ecs() - .read_storage::() + .read_storage::() .get(client.entity()) .map_or(Vec3::zero(), |pos| pos.0); let mut name_id_walker = self.ids.name_tags.walk(); diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index e8e34a3247..62f5468959 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -108,7 +108,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/scene/figure.rs b/voxygen/src/scene/figure.rs index 815c5bcb42..8d07489fa6 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -477,15 +477,15 @@ impl FigureMgr { let player_pos = client .state() .ecs() - .read_storage::() + .read_storage::() .get(client.entity()) .map_or(Vec3::zero(), |pos| pos.0); for (entity, pos, vel, ori, actor, animation_info, stats) in ( &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), &ecs.read_storage::(), &ecs.read_storage::(), ecs.read_storage::().maybe(), @@ -556,10 +556,25 @@ impl FigureMgr { time, animation_info.time, ), + comp::Animation::Roll => character::RollAnimation::update_skeleton( + state.skeleton_mut(), + time, + animation_info.time, + ), + comp::Animation::Crun => character::CrunAnimation::update_skeleton( + state.skeleton_mut(), + (vel.0.magnitude(), time), + animation_info.time, + ), + comp::Animation::Cidle => character::CidleAnimation::update_skeleton( + state.skeleton_mut(), + time, + animation_info.time, + ), comp::Animation::Gliding => { character::GlidingAnimation::update_skeleton( state.skeleton_mut(), - time, + (vel.0.magnitude(), time), animation_info.time, ) } @@ -661,15 +676,15 @@ impl FigureMgr { let player_pos = client .state() .ecs() - .read_storage::() + .read_storage::() .get(client.entity()) .map_or(Vec3::zero(), |pos| pos.0); for (entity, _, _, _, actor, _, _) in ( &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), &ecs.read_storage::(), &ecs.read_storage::(), ecs.read_storage::().maybe(), diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 9d7c09f608..55fe9c7498 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -110,7 +110,7 @@ impl Scene { let player_pos = client .state() .ecs() - .read_storage::() + .read_storage::() .get(client.entity()) .map_or(Vec3::zero(), |pos| pos.0); diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 1eb563bf8a..3bf9504d51 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -8,7 +8,7 @@ use crate::{ Direction, Error, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; -use common::{clock::Clock, comp, comp::phys::Pos, msg::ClientState}; +use common::{clock::Clock, comp, comp::Pos, msg::ClientState}; use log::{error, warn}; use std::{cell::RefCell, rc::Rc, time::Duration}; use vek::*; @@ -16,8 +16,9 @@ use vek::*; 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). @@ -30,6 +31,7 @@ impl SessionState { scene, client, key_state: KeyState::new(), + controller: comp::Controller::default(), hud: Hud::new(window), } } @@ -45,22 +47,8 @@ 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]; - - for event in self - .client - .borrow_mut() - .tick(comp::Control { move_dir }, dt)? - { + fn tick(&mut self, dt: Duration) -> Result<(), Error> { + for event in self.client.borrow_mut().tick(self.controller.clone(), dt)? { match event { client::Event::Chat(msg) => { self.hud.new_message(msg); @@ -127,19 +115,22 @@ 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::Roll, state) => { + self.controller.roll = state; + } + 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 @@ -149,6 +140,16 @@ 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); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 0e8d73cc18..37a61c2140 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -31,6 +31,7 @@ pub struct ControlSettings { pub screenshot: KeyMouse, pub toggle_ingame_ui: KeyMouse, pub attack: KeyMouse, + pub roll: KeyMouse, } impl Default for ControlSettings { @@ -59,6 +60,7 @@ impl Default for ControlSettings { screenshot: KeyMouse::Key(VirtualKeyCode::F4), toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6), attack: KeyMouse::Mouse(MouseButton::Left), + roll: KeyMouse::Mouse(MouseButton::Middle), } } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index fd158a4708..dfc635f477 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -34,6 +34,7 @@ pub enum GameInput { Screenshot, ToggleIngameUi, Attack, + Roll, Respawn, } @@ -134,6 +135,7 @@ impl Window { GameInput::ToggleIngameUi, ); key_map.insert(settings.controls.attack, GameInput::Attack); + key_map.insert(settings.controls.roll, GameInput::Roll); Ok(Self { events_loop,