From 3f2e22f039a13039d48868f72bae42b1b75e78be Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 9 Sep 2019 19:11:40 +0000 Subject: [PATCH] Exponential interpolation for linear damping With an additional approximation to allow for the same size jumps given different framerates. --- Cargo.lock | 7 +- .../element/skillbar/stamina_wheel-0.vox | 3 + .../element/skillbar/stamina_wheel-1.vox | 3 + .../element/skillbar/stamina_wheel-2.vox | 3 + .../element/skillbar/stamina_wheel-3.vox | 3 + .../element/skillbar/stamina_wheel-4.vox | 3 + .../element/skillbar/stamina_wheel-5.vox | 3 + .../element/skillbar/stamina_wheel-6.vox | 3 + .../element/skillbar/stamina_wheel-7.vox | 3 + .../element/skillbar/stamina_wheel-empty.vox | 3 + assets/voxygen/shaders/include/sky.glsl | 2 +- assets/voxygen/shaders/postprocess-frag.glsl | 8 + client/src/lib.rs | 8 + common/Cargo.toml | 2 +- common/src/comp/character_state.rs | 4 +- common/src/comp/controller.rs | 43 +++++ common/src/comp/mod.rs | 2 +- common/src/comp/phys.rs | 2 + common/src/event.rs | 16 +- common/src/msg/ecs_packet.rs | 4 + common/src/state.rs | 94 ++++++++++- common/src/sys/agent.rs | 41 ++++- common/src/sys/cleanup.rs | 8 +- common/src/sys/controller.rs | 53 +++++- common/src/sys/mod.rs | 10 +- common/src/sys/movement.rs | 97 ++++++++++- common/src/sys/phys.rs | 109 ++++++++---- server/src/cmd.rs | 1 + server/src/lib.rs | 47 ++++++ voxygen/src/anim/character/attack.rs | 1 + voxygen/src/anim/character/block.rs | 1 + voxygen/src/anim/character/blockidle.rs | 1 + voxygen/src/anim/character/cidle.rs | 1 + voxygen/src/anim/character/climb.rs | 108 ++++++++++++ voxygen/src/anim/character/crun.rs | 1 + voxygen/src/anim/character/gliding.rs | 35 +++- voxygen/src/anim/character/idle.rs | 1 + voxygen/src/anim/character/jump.rs | 1 + voxygen/src/anim/character/mod.rs | 6 + voxygen/src/anim/character/roll.rs | 1 + voxygen/src/anim/character/run.rs | 52 ++++-- voxygen/src/anim/character/sit.rs | 125 ++++++++++++++ voxygen/src/anim/character/stand.rs | 19 +-- voxygen/src/anim/character/swim.rs | 130 +++++++++++++++ voxygen/src/anim/character/wield.rs | 1 + voxygen/src/anim/mod.rs | 1 + voxygen/src/anim/quadruped/idle.rs | 1 + voxygen/src/anim/quadruped/jump.rs | 1 + voxygen/src/anim/quadruped/run.rs | 1 + voxygen/src/anim/quadrupedmedium/idle.rs | 1 + voxygen/src/anim/quadrupedmedium/jump.rs | 1 + voxygen/src/anim/quadrupedmedium/run.rs | 1 + voxygen/src/hud/img_ids.rs | 9 + voxygen/src/hud/skillbar.rs | 33 +++- voxygen/src/menu/char_selection/scene.rs | 3 + voxygen/src/scene/figure/mod.rs | 156 +++++++++++++----- voxygen/src/session.rs | 57 ++++++- voxygen/src/settings.rs | 10 ++ voxygen/src/window.rs | 20 +++ 59 files changed, 1208 insertions(+), 156 deletions(-) create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-0.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-1.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-2.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-3.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-4.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-5.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-6.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-7.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-empty.vox create mode 100644 voxygen/src/anim/character/climb.rs create mode 100644 voxygen/src/anim/character/sit.rs create mode 100644 voxygen/src/anim/character/swim.rs diff --git a/Cargo.lock b/Cargo.lock index dfa9de1f42..f21a6f5bfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3197,11 +3197,10 @@ dependencies = [ [[package]] name = "sphynx" version = "0.1.0" -source = "git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d#11cdc7422568aaabd376c87242a60f636e68b40d" +source = "git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455#ac4adf54d181339a789907acd27f61fe81daa455" dependencies = [ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", - "shred 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", "sum_type 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3601,7 +3600,7 @@ dependencies = [ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", "specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)", - "sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d)", + "sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455)", "vek 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4258,7 +4257,7 @@ dependencies = [ "checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" "checksum specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "de65613ada4338aa7ba71eca60eca24c60483433eec0077bc4f33cfc31f4bdf0" "checksum specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)" = "" -"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d)" = "" +"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455)" = "" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" diff --git a/assets/voxygen/element/skillbar/stamina_wheel-0.vox b/assets/voxygen/element/skillbar/stamina_wheel-0.vox new file mode 100644 index 0000000000..3a88fc752a --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:428131eff788ab1ad5400e0d82a6d3306671a9f6eed9ac43452e46f967614e4e +size 4236 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-1.vox b/assets/voxygen/element/skillbar/stamina_wheel-1.vox new file mode 100644 index 0000000000..0867fb8665 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1d4b7afaf0a325c357b2865bffd7abc13bbe3874e6fd940ce2aa4ef6ac93524 +size 4240 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-2.vox b/assets/voxygen/element/skillbar/stamina_wheel-2.vox new file mode 100644 index 0000000000..af761fc249 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2cb03507bef8d50ee03bceec8c38ade39ace39b926cf1cfe95c3996ca81ec93 +size 4236 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-3.vox b/assets/voxygen/element/skillbar/stamina_wheel-3.vox new file mode 100644 index 0000000000..908869b087 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7937cf8728cd5aa5c1026c70d2c920b8100a1fb241582c94b5f224a88a4b8c65 +size 4240 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-4.vox b/assets/voxygen/element/skillbar/stamina_wheel-4.vox new file mode 100644 index 0000000000..9190122bb5 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca98b3e5880cc379176b1f6d1a8aafad3918cccabe374ce95c370b9abf19e995 +size 4236 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-5.vox b/assets/voxygen/element/skillbar/stamina_wheel-5.vox new file mode 100644 index 0000000000..58eba1edad --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ff7ec15effe1d73cf6cf1cbde423192d5d2e80177ebea28ec373a3c9039a222 +size 4272 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-6.vox b/assets/voxygen/element/skillbar/stamina_wheel-6.vox new file mode 100644 index 0000000000..7ae3439101 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-6.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6166f65840900908a1817adf83a36f6f892e8a4462a60eb891c52ef6faacf103 +size 4288 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-7.vox b/assets/voxygen/element/skillbar/stamina_wheel-7.vox new file mode 100644 index 0000000000..7dad13a842 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-7.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6091d44c6e1ed205d1ecf15f84aec2df8793bb49e3a307a56f1320ec2798c2a3 +size 4200 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-empty.vox b/assets/voxygen/element/skillbar/stamina_wheel-empty.vox new file mode 100644 index 0000000000..0740042dd6 --- /dev/null +++ b/assets/voxygen/element/skillbar/stamina_wheel-empty.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32ad9d58f8c34a3fdfdeedf18c0c6efc93b5baf7348fce320d462671bda550b4 +size 58684 diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index 1310f82f87..5e63466e70 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -155,7 +155,7 @@ float fog(vec3 f_pos, vec3 focus_pos, uint medium) { float max_fog = 1.0; if (medium == 1u) { - mist_radius = 32.0; + mist_radius = 96.0; min_fog = 0.0; } diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index f653f1d30c..eb0d162df4 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -163,6 +163,10 @@ vec3 hsv2rgb(vec3 c) { void main() { vec2 uv = (f_pos + 1.0) * 0.5; + if (medium.x == 1u) { + uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); + } + vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy, screen_res.xy); //vec4 fxaa_color = texture(src_color, uv); @@ -173,5 +177,9 @@ void main() { vec4 final_color = fxaa_color; //vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a); + if (medium.x == 1u) { + final_color *= vec4(0.2, 0.2, 0.8, 1.0); + } + tgt_color = vec4(final_color.rgb, 1); } diff --git a/client/src/lib.rs b/client/src/lib.rs index fb1aeb03ed..870cb96670 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -194,6 +194,14 @@ impl Client { } } + pub fn is_mounted(&self) -> bool { + self.state + .ecs() + .read_storage::() + .get(self.entity) + .is_some() + } + pub fn view_distance(&self) -> Option { self.view_distance } diff --git a/common/Cargo.toml b/common/Cargo.toml index 99cb28de05..32410710a1 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Joshua Barretto ", "Maciej Ćwięka , pub look_dir: Vec3, + pub sit: bool, pub jump: bool, pub roll: bool, pub glide: bool, + pub climb: bool, + pub climb_down: bool, + pub wall_leap: bool, pub respawn: bool, + pub events: Vec, +} + +impl Controller { + pub fn reset(&mut self) { + *self = Self::default(); + } + + pub fn clear_events(&mut self) { + self.events.clear(); + } + + pub fn push_event(&mut self, event: ControlEvent) { + self.events.push(event); + } } impl Component for Controller { type Storage = FlaggedStorage>; } + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum MountState { + Unmounted, + MountedBy(Uid), +} + +impl Component for MountState { + type Storage = FlaggedStorage>; +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Mounting(pub Uid); + +impl Component for Mounting { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 450b67b271..b6e1d4dcbe 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -16,7 +16,7 @@ pub use admin::Admin; pub use agent::Agent; pub use body::{humanoid, object, quadruped, quadruped_medium, Body}; pub use character_state::{ActionState, CharacterState, MovementState}; -pub use controller::Controller; +pub use controller::{ControlEvent, Controller, MountState, Mounting}; pub use inputs::CanBuild; pub use inventory::{item, Inventory, InventoryUpdate, Item}; pub use last::Last; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index f8787a633a..db6bd2447b 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -38,6 +38,8 @@ impl Component for Scale { #[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct PhysicsState { pub on_ground: bool, + pub on_wall: Option>, + pub in_fluid: bool, } impl Component for PhysicsState { diff --git a/common/src/event.rs b/common/src/event.rs index e28e6e558b..4fb1bc4c09 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -6,8 +6,18 @@ use vek::*; pub enum LocalEvent { Jump(EcsEntity), - Boost { entity: EcsEntity, vel: Vec3 }, - LandOnGround { entity: EcsEntity, vel: Vec3 }, + WallLeap { + entity: EcsEntity, + wall_dir: Vec3, + }, + Boost { + entity: EcsEntity, + vel: Vec3, + }, + LandOnGround { + entity: EcsEntity, + vel: Vec3, + }, } pub enum ServerEvent { @@ -21,6 +31,8 @@ pub enum ServerEvent { }, Respawn(EcsEntity), Shoot(EcsEntity), + Mount(EcsEntity, EcsEntity), + Unmount(EcsEntity), } pub struct EventBus { diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 26acad89f3..54c3591f07 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -27,6 +27,8 @@ sphynx::sum_type! { LightEmitter(comp::LightEmitter), Item(comp::Item), Scale(comp::Scale), + MountState(comp::MountState), + Mounting(comp::Mounting), } } // Automatically derive From for EcsCompPhantom @@ -44,6 +46,8 @@ sphynx::sum_type! { LightEmitter(PhantomData), Item(PhantomData), Scale(PhantomData), + MountState(PhantomData), + Mounting(PhantomData), } } impl sphynx::CompPacket for EcsCompPacket { diff --git a/common/src/state.rs b/common/src/state.rs index e87e6c91e3..9c5b2035c1 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -13,9 +13,10 @@ use hashbrown::{HashMap, HashSet}; use rayon::{ThreadPool, ThreadPoolBuilder}; use serde_derive::{Deserialize, Serialize}; use specs::{ + saveload::Marker, shred::{Fetch, FetchMut}, storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage}, - Component, DispatcherBuilder, Entity as EcsEntity, + Component, DispatcherBuilder, Entity as EcsEntity, Join, }; use sphynx; use std::{sync::Arc, time::Duration}; @@ -42,7 +43,7 @@ pub struct DeltaTime(pub f32); /// upper limit. If delta time exceeds this value, the game's physics will begin to produce time /// lag. Ideally, we'd avoid such a situation. const MAX_DELTA_TIME: f32 = 1.0; -const HUMANOID_JUMP_ACCEL: f32 = 18.0; +const HUMANOID_JUMP_ACCEL: f32 = 16.0; #[derive(Default)] pub struct BlockChange { @@ -119,6 +120,8 @@ impl State { ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); // Register components send from clients -> server ecs.register::(); @@ -145,7 +148,7 @@ impl State { ecs.register::(); // Register synced resources used by the ECS. - ecs.add_resource_synced(TimeOfDay(0.0)); + ecs.insert_synced(TimeOfDay(0.0)); // Register unsynced resources used by the ECS. ecs.add_resource(Time(0.0)); @@ -171,6 +174,11 @@ impl State { let _ = self.ecs.write_storage().insert(entity, comp); } + /// Delete a component attributed to a particular entity. + pub fn delete_component(&mut self, entity: EcsEntity) -> Option { + self.ecs.write_storage().remove(entity) + } + /// Read a component attributed to a particular entity. pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { self.ecs.read_storage().get(entity).cloned() @@ -289,6 +297,68 @@ impl State { // Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events. self.ecs.write_resource::().0 = dt.as_secs_f32().min(MAX_DELTA_TIME); + // Mounted entities. We handle this here because we need access to the Uid registry and I + // forgot how to access that within a system. Anyhow, here goes. + for (entity, mount_state) in ( + &self.ecs.entities(), + &mut self.ecs.write_storage::(), + ) + .join() + { + match mount_state { + comp::MountState::Unmounted => {} + comp::MountState::MountedBy(mounter) => { + if let Some((controller, mounter)) = + self.ecs.entity_from_uid(mounter.id()).and_then(|mounter| { + self.ecs + .read_storage::() + .get(mounter) + .cloned() + .map(|x| (x, mounter)) + }) + { + let pos = self.ecs.read_storage::().get(entity).copied(); + let ori = self.ecs.read_storage::().get(entity).copied(); + let vel = self.ecs.read_storage::().get(entity).copied(); + if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) { + let _ = self + .ecs + .write_storage() + .insert(mounter, comp::Pos(pos.0 + Vec3::unit_z() * 1.0)); + let _ = self.ecs.write_storage().insert(mounter, ori); + let _ = self.ecs.write_storage().insert(mounter, vel); + } + let _ = self + .ecs + .write_storage::() + .insert(entity, controller); + } else { + *mount_state = comp::MountState::Unmounted; + } + } + } + } + + let mut to_unmount = Vec::new(); + for (entity, comp::Mounting(mountee)) in ( + &self.ecs.entities(), + &self.ecs.read_storage::(), + ) + .join() + { + if self + .ecs + .entity_from_uid(mountee.id()) + .filter(|mountee| self.ecs.is_alive(*mountee)) + .is_none() + { + to_unmount.push(entity); + } + } + for entity in to_unmount { + self.ecs.write_storage::().remove(entity); + } + // Run systems to update the world. // Create and run a dispatcher for ecs systems. let mut dispatch_builder = DispatcherBuilder::new().with_pool(self.thread_pool.clone()); @@ -316,6 +386,7 @@ impl State { let events = self.ecs.read_resource::>().recv_all(); for event in events { let mut velocities = self.ecs.write_storage::(); + let mut controllers = self.ecs.write_storage::(); match event { LocalEvent::LandOnGround { entity, vel } => { if let Some(stats) = self.ecs.write_storage::().get_mut(entity) { @@ -325,13 +396,26 @@ impl State { } } } - LocalEvent::Jump(entity) => { if let Some(vel) = velocities.get_mut(entity) { vel.0.z = HUMANOID_JUMP_ACCEL; } } - + LocalEvent::WallLeap { entity, wall_dir } => { + if let (Some(vel), Some(_controller)) = + (velocities.get_mut(entity), controllers.get_mut(entity)) + { + let hspeed = Vec2::::from(vel.0).magnitude(); + if hspeed > 0.001 && hspeed < 0.5 { + vel.0 += vel.0.normalized() + * Vec3::new(1.0, 1.0, 0.0) + * HUMANOID_JUMP_ACCEL + * 1.5 + - wall_dir * 0.03; + vel.0.z = HUMANOID_JUMP_ACCEL * 0.5; + } + } + } LocalEvent::Boost { entity, vel: extra_vel, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index b98c6f077e..1d36365708 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -1,4 +1,6 @@ -use crate::comp::{Agent, CharacterState, Controller, MovementState::Glide, Pos, Stats}; +use crate::comp::{ + Agent, CharacterState, Controller, MountState, MovementState::Glide, Pos, Stats, +}; use rand::{seq::SliceRandom, thread_rng}; use specs::{Entities, Join, ReadStorage, System, WriteStorage}; use vek::*; @@ -13,15 +15,38 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, CharacterState>, WriteStorage<'a, Agent>, WriteStorage<'a, Controller>, + ReadStorage<'a, MountState>, ); fn run( &mut self, - (entities, positions, stats, character_states, mut agents, mut controllers): Self::SystemData, + (entities, positions, stats, character_states, mut agents, mut controllers, mount_states): Self::SystemData, ) { - for (entity, pos, agent, controller) in - (&entities, &positions, &mut agents, &mut controllers).join() + for (entity, pos, agent, controller, mount_state) in ( + &entities, + &positions, + &mut agents, + &mut controllers, + mount_states.maybe(), + ) + .join() { + // Skip mounted entities + if mount_state + .map(|ms| { + if let MountState::Unmounted = ms { + false + } else { + true + } + }) + .unwrap_or(false) + { + continue; + } + + controller.reset(); + match agent { Agent::Wanderer(bearing) => { *bearing += Vec2::new(rand::random::() - 0.5, rand::random::() - 0.5) @@ -29,7 +54,7 @@ impl<'a> System<'a> for Sys { - *bearing * 0.01 - pos.0 * 0.0002; - if bearing.magnitude_squared() != 0.0 { + if bearing.magnitude_squared() > 0.001 { controller.move_dir = bearing.normalized(); } } @@ -47,7 +72,7 @@ impl<'a> System<'a> for Sys { let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude(); controller.move_dir = if dist > 5.0 { Vec2::from(tgt_pos - pos.0).normalized() - } else if dist < 1.5 && dist > 0.0 { + } else if dist < 1.5 && dist > 0.001 { Vec2::from(pos.0 - tgt_pos).normalized() } else { Vec2::zero() @@ -82,7 +107,7 @@ impl<'a> System<'a> for Sys { let dist = Vec2::::from(target_pos.0 - pos.0).magnitude(); if target_stats.is_dead { choose_new = true; - } else if dist < MIN_ATTACK_DIST { + } else if dist < MIN_ATTACK_DIST && dist > 0.001 { // Fight (and slowly move closer) controller.move_dir = Vec2::::from(target_pos.0 - pos.0).normalized() * 0.01; @@ -109,7 +134,7 @@ impl<'a> System<'a> for Sys { * 0.1 - *bearing * 0.005; - controller.move_dir = if bearing.magnitude_squared() > 0.1 { + controller.move_dir = if bearing.magnitude_squared() > 0.001 { bearing.normalized() } else { Vec2::zero() diff --git a/common/src/sys/cleanup.rs b/common/src/sys/cleanup.rs index 5ef189ebb5..dd9f03867c 100644 --- a/common/src/sys/cleanup.rs +++ b/common/src/sys/cleanup.rs @@ -1,14 +1,12 @@ use crate::comp::Controller; -use specs::{Join, System, WriteStorage}; +use specs::{System, WriteStorage}; /// This system will allow NPCs to modify their controller pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = WriteStorage<'a, Controller>; - fn run(&mut self, mut controllers: Self::SystemData) { - for controller in (&mut controllers).join() { - *controller = Controller::default(); - } + fn run(&mut self, _controllers: Self::SystemData) { + // TODO: More stuff here } } diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index a130f2b458..23d5304dca 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -4,12 +4,16 @@ use super::{ }; use crate::{ comp::{ - item, ActionState::*, Body, CharacterState, Controller, Item, MovementState::*, - PhysicsState, Stats, Vel, + item, ActionState::*, Body, CharacterState, ControlEvent, Controller, Item, + MovementState::*, PhysicsState, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, }; -use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; +use specs::{ + saveload::{Marker, MarkerAllocator}, + Entities, Join, Read, ReadStorage, System, WriteStorage, +}; +use sphynx::UidAllocator; use std::time::Duration; use vek::*; @@ -17,6 +21,7 @@ use vek::*; pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( + Read<'a, UidAllocator>, Entities<'a>, Read<'a, EventBus>, Read<'a, EventBus>, @@ -31,6 +36,7 @@ impl<'a> System<'a> for Sys { fn run( &mut self, ( + uid_allocator, entities, server_bus, local_bus, @@ -96,6 +102,20 @@ impl<'a> System<'a> for Sys { character.movement = Jump; } + // Sit + if controller.sit + && physics.on_ground + && character.action == Idle + && character.movement != Sit + && body.is_humanoid() + { + character.movement = Sit; + } else if character.movement == Sit + && (controller.move_dir.magnitude_squared() > 0.0 || !physics.on_ground) + { + character.movement = Run; + } + // Wield if controller.primary && character.action == Idle @@ -180,9 +200,34 @@ impl<'a> System<'a> for Sys { } // Jump - if controller.jump && physics.on_ground && vel.0.z <= 0.0 { + if controller.jump + && physics.on_ground + && vel.0.z <= 0.0 + && !character.movement.is_roll() + { local_emitter.emit(LocalEvent::Jump(entity)); } + + // Wall leap + if controller.wall_leap { + if let (Some(_wall_dir), Climb) = (physics.on_wall, character.movement) { + //local_emitter.emit(LocalEvent::WallLeap { entity, wall_dir }); + } + } + + // Process controller events + for event in std::mem::replace(&mut controller.events, Vec::new()) { + match event { + ControlEvent::Mount(mountee_uid) => { + if let Some(mountee_entity) = + uid_allocator.retrieve_entity_internal(mountee_uid.id()) + { + server_emitter.emit(ServerEvent::Mount(entity, mountee_entity)); + } + } + ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)), + } + } } } } diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index d177fde4b2..adeee4c759 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -21,9 +21,13 @@ const CLEANUP_SYS: &str = "cleanup_sys"; pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]); - dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]); - dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[PHYS_SYS]); + dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[]); dispatch_builder.add(combat::Sys, COMBAT_SYS, &[CONTROLLER_SYS]); dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]); - dispatch_builder.add(cleanup::Sys, CLEANUP_SYS, &[STATS_SYS, MOVEMENT_SYS]); + dispatch_builder.add( + phys::Sys, + PHYS_SYS, + &[CONTROLLER_SYS, MOVEMENT_SYS, COMBAT_SYS, STATS_SYS], + ); + dispatch_builder.add(cleanup::Sys, CLEANUP_SYS, &[PHYS_SYS]); } diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index cd657704b9..393de342c8 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -1,12 +1,13 @@ +use super::phys::GRAVITY; use crate::{ comp::{ - ActionState::*, CharacterState, Controller, MovementState::*, Ori, PhysicsState, Pos, - Stats, Vel, + ActionState::*, CharacterState, Controller, Mounting, MovementState::*, Ori, PhysicsState, + Pos, Stats, Vel, }, state::DeltaTime, terrain::TerrainGrid, }; -use specs::{Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; +use specs::prelude::*; use std::time::Duration; use vek::*; @@ -16,13 +17,17 @@ 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_WATER_ACCEL: f32 = 70.0; +const HUMANOID_WATER_SPEED: f32 = 120.0; +const HUMANOID_CLIMB_ACCEL: f32 = 5.0; const ROLL_SPEED: f32 = 13.0; const GLIDE_ACCEL: f32 = 15.0; const GLIDE_SPEED: f32 = 45.0; const BLOCK_ACCEL: f32 = 30.0; const BLOCK_SPEED: f32 = 75.0; // Gravity is 9.81 * 4, so this makes gravity equal to .15 -const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95; +const GLIDE_ANTIGRAV: f32 = GRAVITY * 0.96; +const CLIMB_SPEED: f32 = 5.0; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; @@ -30,6 +35,7 @@ pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( + Entities<'a>, ReadExpect<'a, TerrainGrid>, Read<'a, DeltaTime>, ReadStorage<'a, Stats>, @@ -39,11 +45,13 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, + ReadStorage<'a, Mounting>, ); fn run( &mut self, ( + entities, _terrain, dt, stats, @@ -53,10 +61,22 @@ impl<'a> System<'a> for Sys { mut positions, mut velocities, mut orientations, + mountings, ): Self::SystemData, ) { // Apply movement inputs - for (stats, controller, physics, mut character, mut _pos, mut vel, mut ori) in ( + for ( + _entity, + stats, + controller, + physics, + mut character, + mut _pos, + mut vel, + mut ori, + mounting, + ) in ( + &entities, &stats, &controllers, &physics_states, @@ -64,6 +84,7 @@ impl<'a> System<'a> for Sys { &mut positions, &mut velocities, &mut orientations, + mountings.maybe(), ) .join() { @@ -71,6 +92,11 @@ impl<'a> System<'a> for Sys { continue; } + if mounting.is_some() { + character.movement = Sit; + continue; + } + if character.movement.is_roll() { vel.0 = Vec3::new(0.0, 0.0, vel.0.z) + controller @@ -94,6 +120,9 @@ impl<'a> System<'a> for Sys { (true, Run) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => { HUMANOID_ACCEL } + (false, Climb) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => { + HUMANOID_CLIMB_ACCEL + } (false, Glide) if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) => { GLIDE_ACCEL } @@ -102,6 +131,11 @@ impl<'a> System<'a> for Sys { { HUMANOID_AIR_ACCEL } + (false, Swim) + if vel.0.magnitude_squared() < HUMANOID_WATER_SPEED.powf(2.0) => + { + HUMANOID_WATER_ACCEL + } _ => 0.0, }; } @@ -112,6 +146,12 @@ impl<'a> System<'a> for Sys { || character.action.is_block() { Vec2::from(controller.look_dir).normalized() + } else if let (Climb, Some(wall_dir)) = (character.movement, physics.on_wall) { + if Vec2::::from(wall_dir).magnitude_squared() > 0.001 { + Vec2::from(wall_dir).normalized() + } else { + Vec2::from(vel.0) + } } else { Vec2::from(vel.0) }; @@ -129,12 +169,16 @@ impl<'a> System<'a> for Sys { // Glide if character.movement == Glide - && vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) + && Vec2::::from(vel.0).magnitude_squared() < GLIDE_SPEED.powf(2.0) && vel.0.z < 0.0 { character.action = Idle; - 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); + let lift = GLIDE_ANTIGRAV + vel.0.z.abs().powf(2.0) * 0.15; + vel.0.z += dt.0 + * lift + * (Vec2::::from(vel.0).magnitude() * 0.075) + .min(1.0) + .max(0.2); } // Roll @@ -149,7 +193,36 @@ impl<'a> System<'a> for Sys { } } - if physics.on_ground && (character.movement == Jump || character.movement == Glide) { + // Climb + if let (true, Some(_wall_dir)) = ( + (controller.climb | controller.climb_down) && vel.0.z <= CLIMB_SPEED, + physics.on_wall, + ) { + if controller.climb_down && !controller.climb { + vel.0 -= dt.0 * vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0); + } else if controller.climb && !controller.climb_down { + vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED); + } else { + vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5; + vel.0 = Lerp::lerp( + vel.0, + Vec3::zero(), + 30.0 * dt.0 / (1.0 - vel.0.z.min(0.0) * 5.0), + ); + } + + character.movement = Climb; + character.action = Idle; + } else if let Climb = character.movement { + character.movement = Jump; + } + + if physics.on_ground + && (character.movement == Jump + || character.movement == Climb + || character.movement == Glide + || character.movement == Swim) + { character.movement = Stand; } @@ -160,6 +233,12 @@ impl<'a> System<'a> for Sys { { character.movement = Jump; } + + if !physics.on_ground && physics.in_fluid { + character.movement = Swim; + } else if let Swim = character.movement { + character.movement = Stand; + } } } } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 141bf8f755..b7047acc38 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,17 +1,17 @@ use { crate::{ - comp::{Body, Ori, PhysicsState, Pos, Scale, Vel}, + comp::{Body, Mounting, Ori, PhysicsState, Pos, Scale, Vel}, event::{EventBus, LocalEvent}, state::DeltaTime, - terrain::TerrainGrid, + terrain::{Block, TerrainGrid}, vol::ReadVol, }, specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}, vek::*, }; -const GRAVITY: f32 = 9.81 * 4.0; - +pub const GRAVITY: f32 = 9.81 * 4.0; +const BOUYANCY: f32 = 0.0; // Friction values used for linear damping. They are unitless quantities. The // value of these quantities must be between zero and one. They represent the // amount an object will slow down within 1/60th of a second. Eg. if the frction @@ -19,6 +19,7 @@ const GRAVITY: f32 = 9.81 * 4.0; // be 0.99. after 1 second the speed will be 0.54, which is 0.99 ^ 60. const FRIC_GROUND: f32 = 0.125; const FRIC_AIR: f32 = 0.0125; +const FRIC_FLUID: f32 = 0.2; // Integrates forces, calculates the new velocity based off of the old velocity // dt = delta time @@ -29,11 +30,7 @@ fn integrate_forces(dt: f32, mut lv: Vec3, grav: f32, damp: f32) -> Vec3 System<'a> for Sys { WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, + ReadStorage<'a, Mounting>, ); fn run( @@ -68,18 +66,20 @@ impl<'a> System<'a> for Sys { mut positions, mut velocities, mut orientations, + mountings, ): Self::SystemData, ) { let mut event_emitter = event_bus.emitter(); // Apply movement inputs - for (entity, scale, _, mut pos, mut vel, mut _ori) in ( + for (entity, scale, _b, mut pos, mut vel, _ori, _) in ( &entities, scales.maybe(), &bodies, &mut positions, &mut velocities, &mut orientations, + !&mountings, ) .join() { @@ -102,12 +102,23 @@ impl<'a> System<'a> for Sys { let old_vel = *vel; // Integrate forces // Friction is assumed to be a constant dependent on location - let friction = if physics_state.on_ground { - FRIC_GROUND + let friction = FRIC_AIR + .max(if physics_state.on_ground { + FRIC_GROUND + } else { + 0.0 + }) + .max(if physics_state.in_fluid { + FRIC_FLUID + } else { + 0.0 + }); + let downward_force = if physics_state.in_fluid { + (1.0 - BOUYANCY) * GRAVITY } else { - FRIC_AIR + GRAVITY }; - vel.0 = integrate_forces(dt.0, vel.0, GRAVITY, friction); + vel.0 = integrate_forces(dt.0, vel.0, downward_force, friction); // Don't move if we're not in a loaded chunk let pos_delta = if terrain @@ -122,15 +133,11 @@ impl<'a> System<'a> for Sys { }; // Function for determining whether the player at a specific position collides with the ground - let collision_with = |pos: Vec3, near_iter| { + let collision_with = |pos: Vec3, hit: fn(&Block) -> bool, near_iter| { for (i, j, k) in near_iter { let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); - if terrain - .get(block_pos) - .map(|vox| vox.is_solid()) - .unwrap_or(false) - { + if terrain.get(block_pos).map(hit).unwrap_or(false) { let player_aabb = Aabb { min: pos + Vec3::new(-player_rad, -player_rad, 0.0), max: pos + Vec3::new(player_rad, player_rad, player_height), @@ -165,7 +172,9 @@ impl<'a> System<'a> for Sys { const MAX_ATTEMPTS: usize = 16; // While the player is colliding with the terrain... - while collision_with(pos.0, near_iter.clone()) && attempts < MAX_ATTEMPTS { + while collision_with(pos.0, |vox| vox.is_solid(), near_iter.clone()) + && attempts < MAX_ATTEMPTS + { // Calculate the player's AABB let player_aabb = Aabb { min: pos.0 + Vec3::new(-player_rad, -player_rad, 0.0), @@ -187,8 +196,6 @@ impl<'a> System<'a> for Sys { }, ) }) - // Determine whether the block's AABB collides with the player's AABB - .filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb)) // Make sure the block is actually solid .filter(|(block_pos, _)| { terrain @@ -196,13 +203,13 @@ impl<'a> System<'a> for Sys { .map(|vox| vox.is_solid()) .unwrap_or(false) }) + // Determine whether the block's AABB collides with the player's AABB + .filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb)) // Find the maximum of the minimum collision axes (this bit is weird, trust me that it works) - .max_by_key(|(_, block_aabb)| { - ((player_aabb - .collision_vector_with_aabb(*block_aabb) + .min_by_key(|(_, block_aabb)| { + ((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5) .map(|e| e.abs()) - .product() - + block_aabb.min.z) + .sum() * 1_000_000.0) as i32 }) .expect("Collision detected, but no colliding blocks found!"); @@ -231,7 +238,7 @@ impl<'a> System<'a> for Sys { // When the resolution direction is non-vertical, we must be colliding with a wall // If the space above is free... - if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), near_iter.clone()) + if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), |vox| vox.is_solid(), near_iter.clone()) // ...and we're being pushed out horizontally... && resolve_dir.z == 0.0 // ...and the vertical resolution direction is sufficiently great... @@ -244,6 +251,7 @@ impl<'a> System<'a> for Sys { // ...and there is a collision with a block beneath our current hitbox... && collision_with( old_pos + resolve_dir - Vec3::unit_z() * 1.05, + |vox| vox.is_solid(), near_iter.clone(), ) { @@ -274,8 +282,11 @@ impl<'a> System<'a> for Sys { if on_ground { physics_state.on_ground = true; // If the space below us is free, then "snap" to the ground - } else if collision_with(pos.0 - Vec3::unit_z() * 1.05, near_iter.clone()) - && vel.0.z < 0.0 + } else if collision_with( + pos.0 - Vec3::unit_z() * 1.05, + |vox| vox.is_solid(), + near_iter.clone(), + ) && vel.0.z < 0.0 && vel.0.z > -1.5 && was_on_ground { @@ -283,13 +294,45 @@ impl<'a> System<'a> for Sys { physics_state.on_ground = true; } + let dirs = [ + Vec3::unit_x(), + Vec3::unit_y(), + -Vec3::unit_x(), + -Vec3::unit_y(), + ]; + + if let (wall_dir, true) = dirs.iter().fold((Vec3::zero(), false), |(a, hit), dir| { + if collision_with(pos.0 + *dir * 0.01, |vox| vox.is_solid(), near_iter.clone()) { + (a + dir, true) + } else { + (a, hit) + } + }) { + physics_state.on_wall = Some(wall_dir); + } else { + physics_state.on_wall = None; + } + + // Figure out if we're in water + physics_state.in_fluid = collision_with(pos.0, |vox| vox.is_fluid(), near_iter.clone()); + let _ = physics_states.insert(entity, physics_state); } // Apply pushback - for (pos, scale, vel, _) in (&positions, scales.maybe(), &mut velocities, &bodies).join() { + for (pos, scale, vel, _, _) in ( + &positions, + scales.maybe(), + &mut velocities, + &bodies, + !&mountings, + ) + .join() + { let scale = scale.map(|s| s.0).unwrap_or(1.0); - for (pos_other, scale_other, _) in (&positions, scales.maybe(), &bodies).join() { + for (pos_other, scale_other, _, _) in + (&positions, scales.maybe(), &bodies, !&mountings).join() + { let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0); let diff = Vec2::::from(pos.0 - pos_other.0); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7eaf39a2aa..93087eeffa 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -414,6 +414,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C server .create_npc(pos, comp::Stats::new(get_npc_name(id), None), body) .with(comp::Vel(vel)) + .with(comp::MountState::Unmounted) .with(agent) .build(); } diff --git a/server/src/lib.rs b/server/src/lib.rs index 67e4e7eb3a..2a9e67c85f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -363,6 +363,53 @@ impl Server { .insert(entity, comp::ForceUpdate); } } + ServerEvent::Mount(mounter, mountee) => { + if state + .ecs() + .read_storage::() + .get(mounter) + .is_none() + { + let not_mounting_yet = if let Some(comp::MountState::Unmounted) = state + .ecs() + .write_storage::() + .get_mut(mountee) + .cloned() + { + true + } else { + false + }; + + if not_mounting_yet { + if let (Some(mounter_uid), Some(mountee_uid)) = ( + state.ecs().uid_from_entity(mounter), + state.ecs().uid_from_entity(mountee), + ) { + state.write_component( + mountee, + comp::MountState::MountedBy(mounter_uid.into()), + ); + state.write_component(mounter, comp::Mounting(mountee_uid.into())); + } + } + } + } + ServerEvent::Unmount(mounter) => { + let mountee_entity = state + .ecs() + .write_storage::() + .get(mounter) + .and_then(|mountee| state.ecs().entity_from_uid(mountee.0.into())); + if let Some(mountee_entity) = mountee_entity { + state + .ecs_mut() + .write_storage::() + .get_mut(mountee_entity) + .map(|ms| *ms = comp::MountState::Unmounted); + } + state.delete_component::(mounter); + } } if let Some(entity) = todo_remove { diff --git a/voxygen/src/anim/character/attack.rs b/voxygen/src/anim/character/attack.rs index 2bfd5c19b2..017c7d0090 100644 --- a/voxygen/src/anim/character/attack.rs +++ b/voxygen/src/anim/character/attack.rs @@ -18,6 +18,7 @@ impl Animation for AttackAnimation { skeleton: &Self::Skeleton, _global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/block.rs b/voxygen/src/anim/character/block.rs index f3ff71c066..36b2bcb246 100644 --- a/voxygen/src/anim/character/block.rs +++ b/voxygen/src/anim/character/block.rs @@ -19,6 +19,7 @@ impl Animation for BlockAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/blockidle.rs b/voxygen/src/anim/character/blockidle.rs index a2e5c4f839..9ef1e51a7f 100644 --- a/voxygen/src/anim/character/blockidle.rs +++ b/voxygen/src/anim/character/blockidle.rs @@ -19,6 +19,7 @@ impl Animation for BlockIdleAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/cidle.rs b/voxygen/src/anim/character/cidle.rs index 26aadb2665..a72e0ff2d2 100644 --- a/voxygen/src/anim/character/cidle.rs +++ b/voxygen/src/anim/character/cidle.rs @@ -19,6 +19,7 @@ impl Animation for CidleAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/climb.rs b/voxygen/src/anim/character/climb.rs new file mode 100644 index 0000000000..2cd28df9e4 --- /dev/null +++ b/voxygen/src/anim/character/climb.rs @@ -0,0 +1,108 @@ +use super::{ + super::{Animation, SkeletonAttr}, + CharacterSkeleton, +}; +use vek::*; + +pub struct ClimbAnimation; + +impl Animation for ClimbAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = (Vec3, Vec3, f64); + + fn update_skeleton( + skeleton: &Self::Skeleton, + (velocity, _orientation, _global_time): Self::Dependency, + anim_time: f64, + rate: &mut f32, + skeleton_attr: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let speed = velocity.magnitude(); + *rate = speed; + + let constant = 1.0; + let wave = (anim_time as f32 * constant as f32 * 1.5).sin(); + let wave_cos = (anim_time as f32 * constant as f32 * 1.5).cos(); + + let wave_test = (((5.0) + / (0.6 + 4.0 * ((anim_time as f32 * constant as f32 * 1.5).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).sin()); + let wave_testc = (((5.0) + / (0.6 + 4.0 * ((anim_time as f32 * constant as f32 * 1.5).cos()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).cos()); + + next.head.offset = Vec3::new( + 0.0, + 1.0 + skeleton_attr.neck_forward, + skeleton_attr.neck_height + 13.5 + wave_cos * 1.3, + ); + next.head.ori = Quaternion::rotation_z(wave * 0.1) + * Quaternion::rotation_x(0.6) + * Quaternion::rotation_y(wave_test * 0.1); + next.head.scale = Vec3::one() * skeleton_attr.head_scale; + + next.chest.offset = Vec3::new(0.0, 1.0, 5.0 + wave_cos * 1.1); + next.chest.ori = Quaternion::rotation_z(wave_test * 0.25) + * Quaternion::rotation_x(-0.15) + * Quaternion::rotation_y(wave_test * -0.12); + next.chest.scale = Vec3::one(); + + next.belt.offset = Vec3::new(0.0, 1.0, 3.5 + wave_cos * 1.1); + next.belt.ori = Quaternion::rotation_z(wave_test * 0.25) * Quaternion::rotation_x(0.0); + next.belt.scale = Vec3::one(); + + next.shorts.offset = Vec3::new(0.0, 1.0, 1.0 + wave_cos * 1.1); + next.shorts.ori = Quaternion::rotation_z(wave_test * 0.25) + * Quaternion::rotation_x(0.1) + * Quaternion::rotation_y(wave_test * 0.10); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new(-8.5, 3.0 + wave_testc * 1.5, 6.0 - wave_test * 4.0); + next.l_hand.ori = Quaternion::rotation_x(0.2 + wave_testc * 0.5); + next.l_hand.scale = Vec3::one(); + + next.r_hand.offset = Vec3::new(8.5, 3.0 - wave_test * 1.5, 6.0 + wave_test * 4.0); + + next.r_hand.ori = Quaternion::rotation_x(0.2 - wave_testc * 0.5); + next.r_hand.scale = Vec3::one(); + + next.l_foot.offset = Vec3::new(-3.4, 1.0, 6.0 + wave_test * 2.5); + next.l_foot.ori = Quaternion::rotation_x(0.2 - wave_testc * 0.50); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, 1.0, 6.0 - wave_test * 2.5); + next.r_foot.ori = Quaternion::rotation_x(0.2 + wave_testc * 0.50); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + 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(-5.0, 0.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); + next.l_shoulder.scale = Vec3::one() * 1.1; + + next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(wave * 0.15); + next.r_shoulder.scale = Vec3::one() * 1.1; + + 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.torso.offset = Vec3::new(0.0, -0.2 + wave * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next + } +} diff --git a/voxygen/src/anim/character/crun.rs b/voxygen/src/anim/character/crun.rs index 5e69198d89..7f33c43187 100644 --- a/voxygen/src/anim/character/crun.rs +++ b/voxygen/src/anim/character/crun.rs @@ -17,6 +17,7 @@ impl Animation for WieldAnimation { skeleton: &Self::Skeleton, (velocity, global_time): Self::Dependency, anim_time: f64, + rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/gliding.rs b/voxygen/src/anim/character/gliding.rs index 2a66fbbc93..08cce65c5d 100644 --- a/voxygen/src/anim/character/gliding.rs +++ b/voxygen/src/anim/character/gliding.rs @@ -9,15 +9,19 @@ pub struct GlidingAnimation; impl Animation for GlidingAnimation { type Skeleton = CharacterSkeleton; - type Dependency = (f32, f64); + type Dependency = (Vec3, Vec3, Vec3, f64); fn update_skeleton( skeleton: &Self::Skeleton, - (velocity, global_time): Self::Dependency, + (velocity, orientation, last_ori, global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); + + let speed = Vec2::::from(velocity).magnitude(); + let wave_slow = (anim_time as f32 * 7.0).sin(); let wave_slow_cos = (anim_time as f32 * 7.0).cos(); let wave_stop = (anim_time as f32 * 1.5).min(PI / 2.0).sin(); @@ -37,6 +41,22 @@ impl Animation for GlidingAnimation { .sin() * 0.25, ); + + let ori = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + + let tilt = if Vec2::new(ori, last_ori) + .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.15) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 0.8; + next.head.offset = Vec3::new( 0.0 + skeleton_attr.neck_right, 0.0 + skeleton_attr.neck_forward, @@ -76,14 +96,14 @@ impl Animation for GlidingAnimation { 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) * velocity * 0.04, + (wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19) * speed * 0.04, ); next.l_foot.scale = Vec3::one(); 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) * velocity * 0.04, + (wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13) * speed * 0.04, ); next.r_foot.scale = Vec3::one(); @@ -104,12 +124,13 @@ impl Animation for GlidingAnimation { next.r_shoulder.scale = Vec3::one() * 1.1; next.draw.offset = Vec3::new(0.0, -13.0 + wave_very_slow * 0.10, 6.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.ori = + Quaternion::rotation_x(1.0) * Quaternion::rotation_y(wave_very_slow_cos * 0.04); next.draw.scale = Vec3::one(); next.torso.offset = Vec3::new(0.0, 6.0, 15.0) / 11.0 * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.05 * velocity + wave_very_slow * 0.10); + next.torso.ori = Quaternion::rotation_x(-0.05 * speed.max(12.0) + wave_very_slow * 0.10) + * Quaternion::rotation_y(tilt * 16.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next diff --git a/voxygen/src/anim/character/idle.rs b/voxygen/src/anim/character/idle.rs index f5ab828a78..85806d6372 100644 --- a/voxygen/src/anim/character/idle.rs +++ b/voxygen/src/anim/character/idle.rs @@ -18,6 +18,7 @@ impl Animation for IdleAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/jump.rs b/voxygen/src/anim/character/jump.rs index 9360cc7568..02f716d417 100644 --- a/voxygen/src/anim/character/jump.rs +++ b/voxygen/src/anim/character/jump.rs @@ -15,6 +15,7 @@ impl Animation for JumpAnimation { skeleton: &Self::Skeleton, _global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index c8ec0efbcf..3a8f50d91a 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -2,12 +2,15 @@ pub mod attack; pub mod block; pub mod blockidle; pub mod cidle; +pub mod climb; pub mod gliding; pub mod idle; pub mod jump; pub mod roll; pub mod run; +pub mod sit; pub mod stand; +pub mod swim; pub mod wield; // Reexports @@ -15,12 +18,15 @@ pub use self::attack::AttackAnimation; pub use self::block::BlockAnimation; pub use self::blockidle::BlockIdleAnimation; pub use self::cidle::CidleAnimation; +pub use self::climb::ClimbAnimation; 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; +pub use self::sit::SitAnimation; pub use self::stand::StandAnimation; +pub use self::swim::SwimAnimation; pub use self::wield::WieldAnimation; use super::{Bone, Skeleton}; diff --git a/voxygen/src/anim/character/roll.rs b/voxygen/src/anim/character/roll.rs index fd5989b1d6..ab397172eb 100644 --- a/voxygen/src/anim/character/roll.rs +++ b/voxygen/src/anim/character/roll.rs @@ -15,6 +15,7 @@ impl Animation for RollAnimation { skeleton: &Self::Skeleton, _global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/run.rs b/voxygen/src/anim/character/run.rs index 759ae0c396..2f2edddb63 100644 --- a/voxygen/src/anim/character/run.rs +++ b/voxygen/src/anim/character/run.rs @@ -10,21 +10,35 @@ pub struct RunAnimation; impl Animation for RunAnimation { type Skeleton = CharacterSkeleton; - type Dependency = (f32, f32, f64); + type Dependency = (Vec3, Vec3, Vec3, f64); fn update_skeleton( skeleton: &Self::Skeleton, - (velocity, orientation, global_time): Self::Dependency, + (velocity, orientation, last_ori, global_time): Self::Dependency, anim_time: f64, + rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave = (anim_time as f32 * velocity * 1.2).sin(); - let wave_cos = (anim_time as f32 * velocity * 1.2).cos(); + let speed = Vec2::::from(velocity).magnitude(); + *rate = speed; - let wave_diff = (anim_time as f32 * velocity * 0.6).sin(); - let wave_cos_dub = (anim_time as f32 * velocity * 2.4).cos(); + let constant = 1.0; + let wave = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 1.2).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.2).sin()); + let wave_cos = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 2.4).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).sin()); + let wave_cos_dub = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 1.5).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).sin()); + + let wave_diff = (anim_time as f32 * 0.6).sin(); 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) @@ -39,18 +53,20 @@ impl Animation for RunAnimation { * 0.1, ); - let vel = Vec2::from(velocity); - let ori = (Vec2::from(orientation)).normalized(); + let ori = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); - let _tilt = if Vec2::new(ori, vel) - .map(|v| Vec2::::from(v).magnitude_squared()) - .reduce_partial_min() - > 0.001 + let tilt = if Vec2::new(ori, last_ori) + .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() { - vel.normalized().dot(ori.normalized()).min(1.0).acos() + ori.angle_between(last_ori).min(0.5) + * last_ori.determine_side(Vec2::zero(), ori).signum() } else { 0.0 - }; + } * 1.3; next.head.offset = Vec3::new( 0.0, @@ -90,11 +106,11 @@ impl Animation for RunAnimation { next.r_hand.scale = Vec3::one(); next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); - next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5); + next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.2); next.l_foot.scale = Vec3::one(); next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); - next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5); + next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.2); next.r_foot.scale = Vec3::one(); next.weapon.offset = Vec3::new( @@ -120,8 +136,8 @@ impl Animation for RunAnimation { next.torso.offset = Vec3::new(0.0, -0.2 + wave * -0.08, 0.4) * skeleton_attr.scaler; next.torso.ori = - Quaternion::rotation_x(wave_stop * velocity * -0.06 + wave_diff * velocity * -0.005) - * Quaternion::rotation_y(0.0); + Quaternion::rotation_x(wave_stop * speed * -0.06 + wave_diff * speed * -0.005) + * Quaternion::rotation_y(tilt); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next diff --git a/voxygen/src/anim/character/sit.rs b/voxygen/src/anim/character/sit.rs new file mode 100644 index 0000000000..be24d9801d --- /dev/null +++ b/voxygen/src/anim/character/sit.rs @@ -0,0 +1,125 @@ +use super::{ + super::{Animation, SkeletonAttr}, + CharacterSkeleton, +}; +use std::{f32::consts::PI, ops::Mul}; +use vek::*; + +pub struct SitAnimation; + +impl Animation for SitAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = f64; + + fn update_skeleton( + skeleton: &Self::Skeleton, + global_time: f64, + anim_time: f64, + _rate: &mut f32, + skeleton_attr: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave_slow = (anim_time as f32 * 1.0 + PI).sin(); + let wave_slow_cos = (anim_time as f32 * 1.0 + PI).cos(); + let wave_stop = (anim_time as f32 * 3.0).min(PI / 2.0).sin(); + let wave_slow_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; + let wave_ultra_slow = (anim_time as f32 * 0.3 + PI).sin(); + let wave_ultra_slow_cos = (anim_time as f32 * 0.3 + PI).cos(); + + let head_look = Vec2::new( + ((global_time + anim_time) as f32 / 18.0) + .floor() + .mul(7331.0) + .sin() + * 0.25, + ((global_time + anim_time) as f32 / 18.0) + .floor() + .mul(1337.0) + .sin() + * 0.125, + ); + next.head.offset = Vec3::new( + 0.0 + skeleton_attr.neck_right, + wave_stop * -1.6 + skeleton_attr.neck_forward, + skeleton_attr.neck_height + 15.0 + wave_slow * 0.1 + wave_stop * -0.8, + ); + next.head.ori = + Quaternion::rotation_z(head_look.x + wave_ultra_slow * 0.2 - wave_slow * 0.1) + * Quaternion::rotation_x( + (wave_ultra_slow_cos * -0.2 + wave_slow * 0.1 + head_look.y).abs(), + ); + next.head.scale = Vec3::one() * skeleton_attr.head_scale; + + next.chest.offset = Vec3::new( + 0.0, + wave_stop * -0.4, + 7.0 + wave_slow * 0.1 + wave_stop * -0.8, + ); + next.chest.ori = Quaternion::rotation_x(wave_stop * 0.15); + next.chest.scale = Vec3::one() + wave_slow_abs * 0.05; + + next.belt.offset = Vec3::new(0.0, wave_stop * 1.2, 5.0); + next.belt.ori = Quaternion::rotation_x(wave_stop * 0.3); + next.belt.scale = (Vec3::one() + wave_slow_abs * 0.05) * 1.02; + + next.shorts.offset = Vec3::new(0.0, wave_stop * 2.5, 2.0 + wave_stop * 0.6); + next.shorts.ori = Quaternion::rotation_x(wave_stop * 0.6); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new( + -7.5, + 0.0 + wave_ultra_slow_cos * 0.15, + wave_ultra_slow * 0.7 + wave_stop * -2.0, + ); + + next.l_hand.ori = + Quaternion::rotation_x(0.0 + wave_slow_cos * -0.1 + wave_ultra_slow * 0.1); + next.l_hand.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.r_hand.offset = Vec3::new( + 7.5, + 0.0 + wave_ultra_slow_cos * 0.15, + wave_ultra_slow * 0.7 + wave_stop * -2.0, + ); + next.r_hand.ori = + Quaternion::rotation_x(0.0 + wave_slow * -0.1 + wave_ultra_slow_cos * 0.1); + next.r_hand.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); + next.l_foot.ori = + Quaternion::rotation_x(wave_slow * 0.1 + wave_stop * 1.2 + wave_ultra_slow * 0.1); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, -0.1, 8.0); + next.r_foot.ori = Quaternion::rotation_x( + wave_slow_cos * 0.1 + wave_stop * 1.2 + wave_ultra_slow_cos * 0.1, + ); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.weapon.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.scale = (Vec3::one() + wave_slow_abs * -0.05) * 1.15; + + next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.scale = (Vec3::one() + wave_slow_abs * -0.05) * 1.15; + + 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.torso.offset = Vec3::new(0.0, -0.2, wave_stop * -0.16) * skeleton_attr.scaler; + next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next + } +} diff --git a/voxygen/src/anim/character/stand.rs b/voxygen/src/anim/character/stand.rs index c7057b1f1e..00163be28c 100644 --- a/voxygen/src/anim/character/stand.rs +++ b/voxygen/src/anim/character/stand.rs @@ -5,9 +5,6 @@ use super::{ use std::{f32::consts::PI, ops::Mul}; use vek::*; -pub struct Input { - pub attack: bool, -} pub struct StandAnimation; impl Animation for StandAnimation { @@ -18,12 +15,14 @@ impl Animation for StandAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> 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 wave_ultra_slow_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; let head_look = Vec2::new( ((global_time + anim_time) as f32 / 12.0) @@ -48,11 +47,11 @@ impl Animation for StandAnimation { 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.chest.scale = Vec3::one() + wave_ultra_slow_abs * 0.05; 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.belt.scale = Vec3::one() + wave_ultra_slow_abs * 0.05; next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_ultra_slow * 0.3); next.shorts.ori = Quaternion::rotation_x(0.0); @@ -70,10 +69,10 @@ impl Animation for StandAnimation { next.r_hand.offset = Vec3::new( 7.5, 0.0 + wave_ultra_slow_cos * 0.15, - 0.0 + wave_ultra_slow * 0.5, + 0.0 + wave_ultra_slow * 0.5 + wave_ultra_slow_abs * -0.05, ); next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); - next.r_hand.scale = Vec3::one(); + next.r_hand.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); next.l_foot.ori = Quaternion::identity(); @@ -89,15 +88,15 @@ impl Animation for StandAnimation { 15.0, ); next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.weapon.scale = Vec3::one(); + next.weapon.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(0.0); - next.l_shoulder.scale = Vec3::one() * 1.1; + next.l_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); next.r_shoulder.ori = Quaternion::rotation_x(0.0); - next.r_shoulder.scale = Vec3::one() * 1.1; + next.r_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; next.draw.offset = Vec3::new(0.0, 5.0, 0.0); next.draw.ori = Quaternion::rotation_y(0.0); diff --git a/voxygen/src/anim/character/swim.rs b/voxygen/src/anim/character/swim.rs new file mode 100644 index 0000000000..74a995028a --- /dev/null +++ b/voxygen/src/anim/character/swim.rs @@ -0,0 +1,130 @@ +use super::{ + super::{Animation, SkeletonAttr}, + CharacterSkeleton, +}; +use std::f32::consts::PI; +use std::ops::Mul; +use vek::*; + +pub struct SwimAnimation; + +impl Animation for SwimAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = (f32, f32, f64); + + fn update_skeleton( + skeleton: &Self::Skeleton, + (velocity, orientation, global_time): Self::Dependency, + anim_time: f64, + _rate: &mut f32, + skeleton_attr: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave = (anim_time as f32 * velocity * 1.2).sin(); + let wave_cos = (anim_time as f32 * velocity * 1.2).cos(); + + let wave_diff = (anim_time as f32 * velocity * 0.6).sin(); + let wave_cos_dub = (anim_time as f32 * velocity * 2.4).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) + .floor() + .mul(7331.0) + .sin() + * 0.2, + ((global_time + anim_time) as f32 / 2.0) + .floor() + .mul(1337.0) + .sin() + * 0.1, + ); + + let vel = Vec2::from(velocity); + let ori = (Vec2::from(orientation)).normalized(); + + let _tilt = if Vec2::new(ori, vel) + .map(|v| Vec2::::from(v).magnitude_squared()) + .reduce_partial_min() + > 0.001 + { + vel.normalized().dot(ori.normalized()).min(1.0).acos() + } else { + 0.0 + }; + + next.head.offset = Vec3::new( + 0.0, + -1.0 + skeleton_attr.neck_forward, + skeleton_attr.neck_height + 15.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() * skeleton_attr.head_scale; + + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_cos * 1.1); + next.chest.ori = Quaternion::rotation_z(wave * 0.2); + 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.35); + 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( + -7.5 + wave_cos_dub * 1.0, + 2.0 + wave_cos * 5.0, + 0.0 - wave * 1.5, + ); + next.l_hand.ori = Quaternion::rotation_x(wave_cos * 0.8); + next.l_hand.scale = Vec3::one(); + + next.r_hand.offset = Vec3::new( + 7.5 - wave_cos_dub * 1.0, + 2.0 - wave_cos * 5.0, + 0.0 + wave * 1.5, + ); + next.r_hand.ori = Quaternion::rotation_x(wave_cos * -0.8); + next.r_hand.scale = Vec3::one(); + + next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); + 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 - wave_cos_dub * 0.7); + 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 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + 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(-5.0, 0.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); + next.l_shoulder.scale = Vec3::one() * 1.1; + + next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(wave * 0.15); + next.r_shoulder.scale = Vec3::one() * 1.1; + + 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.torso.offset = Vec3::new(0.0, -0.2 + wave * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.ori = + Quaternion::rotation_x(wave_stop * velocity * -0.06 + wave_diff * velocity * -0.005) + * Quaternion::rotation_y(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next + } +} diff --git a/voxygen/src/anim/character/wield.rs b/voxygen/src/anim/character/wield.rs index 56bce3625e..bac379c2ff 100644 --- a/voxygen/src/anim/character/wield.rs +++ b/voxygen/src/anim/character/wield.rs @@ -15,6 +15,7 @@ impl Animation for WieldAnimation { skeleton: &Self::Skeleton, (_velocity, _global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index d3d27e0ad2..12287b1c15 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -178,6 +178,7 @@ pub trait Animation { skeleton: &Self::Skeleton, dependency: Self::Dependency, anim_time: f64, + rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton; } diff --git a/voxygen/src/anim/quadruped/idle.rs b/voxygen/src/anim/quadruped/idle.rs index 8477a2c9c1..fc0f912acc 100644 --- a/voxygen/src/anim/quadruped/idle.rs +++ b/voxygen/src/anim/quadruped/idle.rs @@ -15,6 +15,7 @@ impl Animation for IdleAnimation { skeleton: &Self::Skeleton, global_time: Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadruped/jump.rs b/voxygen/src/anim/quadruped/jump.rs index 19d9075428..d0cf3fbe39 100644 --- a/voxygen/src/anim/quadruped/jump.rs +++ b/voxygen/src/anim/quadruped/jump.rs @@ -15,6 +15,7 @@ impl Animation for JumpAnimation { skeleton: &Self::Skeleton, (_velocity, _global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadruped/run.rs b/voxygen/src/anim/quadruped/run.rs index 327f2bf559..f43863a063 100644 --- a/voxygen/src/anim/quadruped/run.rs +++ b/voxygen/src/anim/quadruped/run.rs @@ -14,6 +14,7 @@ impl Animation for RunAnimation { skeleton: &Self::Skeleton, (_velocity, _global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadrupedmedium/idle.rs b/voxygen/src/anim/quadrupedmedium/idle.rs index 568d1c511d..63c1bcd97e 100644 --- a/voxygen/src/anim/quadrupedmedium/idle.rs +++ b/voxygen/src/anim/quadrupedmedium/idle.rs @@ -15,6 +15,7 @@ impl Animation for IdleAnimation { skeleton: &Self::Skeleton, global_time: Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadrupedmedium/jump.rs b/voxygen/src/anim/quadrupedmedium/jump.rs index 7708083852..06f93aa6f7 100644 --- a/voxygen/src/anim/quadrupedmedium/jump.rs +++ b/voxygen/src/anim/quadrupedmedium/jump.rs @@ -15,6 +15,7 @@ impl Animation for JumpAnimation { skeleton: &Self::Skeleton, _global_time: Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadrupedmedium/run.rs b/voxygen/src/anim/quadrupedmedium/run.rs index 5fd7a5d8cd..5acc6d4cd8 100644 --- a/voxygen/src/anim/quadrupedmedium/run.rs +++ b/voxygen/src/anim/quadrupedmedium/run.rs @@ -15,6 +15,7 @@ impl Animation for RunAnimation { skeleton: &Self::Skeleton, (_velocity, global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 0c7fb13710..e38477fb19 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -28,6 +28,15 @@ image_ids! { bar_content: "voxygen.element.skillbar.bar_content", level_up: "voxygen.element.misc_bg.level_up", level_down:"voxygen.element.misc_bg.level_down", + stamina_0:"voxygen.element.skillbar.stamina_wheel-empty", + stamina_1:"voxygen.element.skillbar.stamina_wheel-0", + stamina_2:"voxygen.element.skillbar.stamina_wheel-1", + stamina_3:"voxygen.element.skillbar.stamina_wheel-2", + stamina_4:"voxygen.element.skillbar.stamina_wheel-3", + stamina_5:"voxygen.element.skillbar.stamina_wheel-4", + stamina_6:"voxygen.element.skillbar.stamina_wheel-5", + stamina_7:"voxygen.element.skillbar.stamina_wheel-6", + stamina_8:"voxygen.element.skillbar.stamina_wheel-7", // Window Parts window_3: "voxygen.element.frames.window_3", diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index d492e76277..912a14ca58 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -77,7 +77,7 @@ widget_ids! { level_down, level_align, level_message, - + stamina_wheel, } } @@ -163,6 +163,37 @@ impl<'a> Widget for Skillbar<'a> { let shortcuts = self.global_state.settings.gameplay.shortcut_numbers; const BG_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 0.8); + + // Stamina Wheel + /* + let stamina_percentage = + self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0; + if stamina_percentage < 100.0 { + Image::new(if stamina_percentage <= 0.1 { + self.imgs.stamina_0 + } else if stamina_percentage < 12.5 { + self.imgs.stamina_1 + } else if stamina_percentage < 25.0 { + self.imgs.stamina_2 + } else if stamina_percentage < 37.5 { + self.imgs.stamina_3 + } else if stamina_percentage < 50.0 { + self.imgs.stamina_4 + } else if stamina_percentage < 62.5 { + self.imgs.stamina_5 + } else if stamina_percentage < 75.0 { + self.imgs.stamina_6 + } else if stamina_percentage < 87.5 { + self.imgs.stamina_7 + } else { + self.imgs.stamina_8 + }) + .w_h(37.0 * 3.0, 37.0 * 3.0) + .mid_bottom_with_margin_on(ui.window, 150.0) + .set(state.ids.stamina_wheel, ui); + } + */ + // Level Up Message let current_level = self.stats.level.level(); diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index cdc6471d5e..9d57cda816 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -131,6 +131,7 @@ impl Scene { self.figure_state.skeleton_mut(), client.state().get_time(), client.state().get_time(), + &mut 0.0, &SkeletonAttr::from(&body), ); self.figure_state.skeleton_mut().interpolate( @@ -145,6 +146,8 @@ impl Scene { 1.0, Rgba::broadcast(1.0), 1.0 / 60.0, // TODO: Use actual deltatime here? + 1.0, + 1.0, ); } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index b62bffc3ba..fb3d402899 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -23,7 +23,6 @@ use common::{ use hashbrown::HashMap; use log::debug; use specs::{Entity as EcsEntity, Join}; -use std::time::Instant; use vek::*; const DAMAGE_FADE_COEFFICIENT: f64 = 5.0; @@ -128,6 +127,9 @@ impl FigureMgr { ) .1; + let mut movement_animation_rate = 1.0; + let mut action_animation_rate = 1.0; + match body { Body::Humanoid(_) => { let state = self @@ -140,45 +142,67 @@ impl FigureMgr { }; if !character.is_same_movement(&last_character.0) { - state.last_movement_change = Instant::now(); + state.movement_time = 0.0; } if !character.is_same_action(&last_character.0) { - state.last_action_change = Instant::now(); + state.action_time = 0.0; } - let time_since_movement_change = - state.last_movement_change.elapsed().as_secs_f64(); - let time_since_action_change = state.last_action_change.elapsed().as_secs_f64(); - let target_base = match &character.movement { Stand => anim::character::StandAnimation::update_skeleton( &CharacterSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Run => anim::character::RunAnimation::update_skeleton( &CharacterSkeleton::new(), - (vel.0.magnitude(), ori.0.magnitude(), time), - time_since_movement_change, + (vel.0, ori.0, state.last_ori, time), + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Jump => anim::character::JumpAnimation::update_skeleton( &CharacterSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Roll { .. } => anim::character::RollAnimation::update_skeleton( &CharacterSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Glide => anim::character::GlidingAnimation::update_skeleton( &CharacterSkeleton::new(), - (vel.0.magnitude(), time), - time_since_movement_change, + (vel.0, ori.0, state.last_ori, time), + state.movement_time, + &mut movement_animation_rate, + skeleton_attr, + ), + Swim => anim::character::SwimAnimation::update_skeleton( + &CharacterSkeleton::new(), + (vel.0.magnitude(), ori.0.magnitude(), time), + state.movement_time, + &mut movement_animation_rate, + skeleton_attr, + ), + Climb => anim::character::ClimbAnimation::update_skeleton( + &CharacterSkeleton::new(), + (vel.0, ori.0, time), + state.movement_time, + &mut movement_animation_rate, + skeleton_attr, + ), + Sit => anim::character::SitAnimation::update_skeleton( + &CharacterSkeleton::new(), + time, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), }; @@ -187,40 +211,54 @@ impl FigureMgr { (Stand, Wield { .. }) => anim::character::CidleAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), (Stand, Block { .. }) => { anim::character::BlockIdleAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ) } (_, Attack { .. }) => anim::character::AttackAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), (_, Wield { .. }) => anim::character::WieldAnimation::update_skeleton( &target_base, (vel.0.magnitude(), time), - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), (_, Block { .. }) => anim::character::BlockAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), _ => target_base, }; state.skeleton.interpolate(&target_bones, dt); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } Body::Quadruped(_) => { let state = self @@ -234,29 +272,29 @@ impl FigureMgr { }; if !character.is_same_movement(&last_character.0) { - state.last_movement_change = Instant::now(); + state.movement_time = 0.0; } - let time_since_movement_change = - state.last_movement_change.elapsed().as_secs_f64(); - let target_base = match character.movement { Stand => anim::quadruped::IdleAnimation::update_skeleton( &QuadrupedSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Run => anim::quadruped::RunAnimation::update_skeleton( &QuadrupedSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Jump => anim::quadruped::JumpAnimation::update_skeleton( &QuadrupedSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), @@ -265,7 +303,16 @@ impl FigureMgr { }; state.skeleton.interpolate(&target_base, dt); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } Body::QuadrupedMedium(_) => { let state = self @@ -281,29 +328,29 @@ impl FigureMgr { }; if !character.is_same_movement(&last_character.0) { - state.last_movement_change = Instant::now(); + state.movement_time = 0.0; } - let time_since_movement_change = - state.last_movement_change.elapsed().as_secs_f64(); - let target_base = match character.movement { Stand => anim::quadrupedmedium::IdleAnimation::update_skeleton( &QuadrupedMediumSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Run => anim::quadrupedmedium::RunAnimation::update_skeleton( &QuadrupedMediumSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Jump => anim::quadrupedmedium::JumpAnimation::update_skeleton( &QuadrupedMediumSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), @@ -312,7 +359,16 @@ impl FigureMgr { }; state.skeleton.interpolate(&target_base, dt); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } Body::Object(_) => { let state = self @@ -321,7 +377,16 @@ impl FigureMgr { .or_insert_with(|| FigureState::new(renderer, ObjectSkeleton::new())); state.skeleton = state.skeleton_mut().clone(); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } } } @@ -426,11 +491,12 @@ impl FigureMgr { pub struct FigureState { bone_consts: Consts, locals: Consts, - last_movement_change: Instant, - last_action_change: Instant, + movement_time: f64, + action_time: f64, skeleton: S, pos: Vec3, ori: Vec3, + last_ori: Vec3, } impl FigureState { @@ -440,11 +506,12 @@ impl FigureState { .create_consts(&skeleton.compute_matrices()) .unwrap(), locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), - last_movement_change: Instant::now(), - last_action_change: Instant::now(), + movement_time: 0.0, + action_time: 0.0, skeleton, pos: Vec3::zero(), ori: Vec3::zero(), + last_ori: Vec3::zero(), } } @@ -456,7 +523,11 @@ impl FigureState { scale: f32, col: Rgba, dt: f32, + movement_rate: f32, + action_rate: f32, ) { + self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt); + // Update interpolation values if self.pos.distance_squared(pos) < 64.0 * 64.0 { self.pos = Lerp::lerp(self.pos, pos, 15.0 * dt); @@ -466,6 +537,9 @@ impl FigureState { self.ori = ori; } + self.movement_time += (dt * movement_rate) as f64; + self.action_time += (dt * action_rate) as f64; + let mat = Mat4::::identity() * Mat4::translation_3d(self.pos) * Mat4::rotation_z(-ori.x.atan2(ori.y)) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 9710e831ab..029d5fe67d 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -10,8 +10,7 @@ use client::{self, Client}; use common::{ clock::Clock, comp, - comp::Pos, - comp::Vel, + comp::{Pos, Vel}, msg::ClientState, terrain::{Block, BlockKind}, vol::ReadVol, @@ -118,6 +117,9 @@ impl PlayState for SessionState { .compute_dependents(&self.client.borrow()); let cam_dir: Vec3 = Vec3::from(view_mat.inverted() * -Vec4::unit_z()); + // Reset controller events + self.controller.clear_events(); + // Handle window events. for event in global_state.window.fetch_events() { // Pass all events to the ui first. @@ -208,6 +210,9 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Jump, state) => { self.controller.jump = state; } + Event::InputUpdate(GameInput::Sit, state) => { + self.controller.sit = 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, @@ -215,6 +220,54 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Glide, state) => { self.controller.glide = state; } + Event::InputUpdate(GameInput::Climb, state) => self.controller.climb = state, + Event::InputUpdate(GameInput::ClimbDown, state) => { + self.controller.climb_down = state + } + Event::InputUpdate(GameInput::WallLeap, state) => { + self.controller.wall_leap = state + } + Event::InputUpdate(GameInput::Mount, true) => { + let client = self.client.borrow(); + if client.is_mounted() { + self.controller.push_event(comp::ControlEvent::Unmount); + } else { + let player_pos = client + .state() + .read_storage::() + .get(client.entity()) + .copied(); + if let Some(player_pos) = player_pos { + // Find closest mountable entity + let closest_mountable = ( + &client.state().ecs().entities(), + &client.state().ecs().read_storage::(), + &client.state().ecs().read_storage::(), + ) + .join() + .filter(|(_, _, ms)| { + if let comp::MountState::Unmounted = ms { + true + } else { + false + } + }) + .min_by_key(|(_, pos, _)| { + (player_pos.0.distance_squared(pos.0) * 1000.0) as i32 + }) + .map(|(entity, _, _)| entity); + + if let Some(mountee_uid) = + closest_mountable.and_then(|mountee_entity| { + client.state().ecs().uid_from_entity(mountee_entity) + }) + { + self.controller + .push_event(comp::ControlEvent::Mount(mountee_uid.into())); + } + } + } + } Event::InputUpdate(GameInput::Interact, state) => { let mut client = self.client.borrow_mut(); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 66327d7fe4..df23337137 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -24,7 +24,12 @@ pub struct ControlSettings { pub move_back: KeyMouse, pub move_right: KeyMouse, pub jump: KeyMouse, + pub sit: KeyMouse, pub glide: KeyMouse, + pub climb: KeyMouse, + pub climb_down: KeyMouse, + pub wall_leap: KeyMouse, + pub mount: KeyMouse, pub map: KeyMouse, pub bag: KeyMouse, pub quest_log: KeyMouse, @@ -57,7 +62,12 @@ impl Default for ControlSettings { move_back: KeyMouse::Key(VirtualKeyCode::S), move_right: KeyMouse::Key(VirtualKeyCode::D), jump: KeyMouse::Key(VirtualKeyCode::Space), + sit: KeyMouse::Key(VirtualKeyCode::K), glide: KeyMouse::Key(VirtualKeyCode::LShift), + climb: KeyMouse::Key(VirtualKeyCode::Space), + climb_down: KeyMouse::Key(VirtualKeyCode::LShift), + wall_leap: KeyMouse::Mouse(MouseButton::Middle), + mount: KeyMouse::Key(VirtualKeyCode::F), map: KeyMouse::Key(VirtualKeyCode::M), bag: KeyMouse::Key(VirtualKeyCode::B), quest_log: KeyMouse::Key(VirtualKeyCode::L), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 6676c98223..2bf555ca8f 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -19,7 +19,12 @@ pub enum GameInput { MoveLeft, MoveRight, Jump, + Sit, Glide, + Climb, + ClimbDown, + WallLeap, + Mount, Enter, Command, Escape, @@ -142,9 +147,24 @@ impl Window { map.entry(settings.controls.jump) .or_default() .push(GameInput::Jump); + map.entry(settings.controls.sit) + .or_default() + .push(GameInput::Sit); map.entry(settings.controls.glide) .or_default() .push(GameInput::Glide); + map.entry(settings.controls.climb) + .or_default() + .push(GameInput::Climb); + map.entry(settings.controls.climb_down) + .or_default() + .push(GameInput::ClimbDown); + map.entry(settings.controls.wall_leap) + .or_default() + .push(GameInput::WallLeap); + map.entry(settings.controls.mount) + .or_default() + .push(GameInput::Mount); map.entry(settings.controls.map) .or_default() .push(GameInput::Map);