Exponential interpolation for linear damping

With an additional approximation to allow for the same size jumps given different framerates.
This commit is contained in:
Joshua Barretto 2019-09-09 19:11:40 +00:00
parent f81a654bc1
commit e6d8b4b8d8
59 changed files with 1181 additions and 156 deletions

7
Cargo.lock generated

@ -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)" = "<none>"
"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d)" = "<none>"
"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455)" = "<none>"
"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"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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;
}

@ -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);
}

@ -194,6 +194,14 @@ impl Client {
}
}
pub fn is_mounted(&self) -> bool {
self.state
.ecs()
.read_storage::<comp::Mounting>()
.get(self.entity)
.is_some()
}
pub fn view_distance(&self) -> Option<u32> {
self.view_distance
}

@ -5,7 +5,7 @@ authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mc
edition = "2018"
[dependencies]
sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"], rev = "11cdc7422568aaabd376c87242a60f636e68b40d" }
sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"], rev = "ac4adf54d181339a789907acd27f61fe81daa455" }
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" }
specs = { version = "0.14.2", features = ["serde", "nightly"] }

@ -5,11 +5,13 @@ use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum MovementState {
Stand,
Sit,
Run,
Jump,
Glide,
Roll { time_left: Duration },
//Swim,
Swim,
Climb,
}
impl MovementState {

@ -1,19 +1,62 @@
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
use sphynx::Uid;
use vek::*;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ControlEvent {
Mount(Uid),
Unmount,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Controller {
pub primary: bool,
pub secondary: bool,
pub move_dir: Vec2<f32>,
pub look_dir: Vec3<f32>,
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<ControlEvent>,
}
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<Self, IDVStorage<Self>>;
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum MountState {
Unmounted,
MountedBy(Uid),
}
impl Component for MountState {
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Mounting(pub Uid);
impl Component for Mounting {
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
}

@ -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;

@ -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<Vec3<f32>>,
pub in_fluid: bool,
}
impl Component for PhysicsState {

@ -6,8 +6,18 @@ use vek::*;
pub enum LocalEvent {
Jump(EcsEntity),
Boost { entity: EcsEntity, vel: Vec3<f32> },
LandOnGround { entity: EcsEntity, vel: Vec3<f32> },
WallLeap {
entity: EcsEntity,
wall_dir: Vec3<f32>,
},
Boost {
entity: EcsEntity,
vel: Vec3<f32>,
},
LandOnGround {
entity: EcsEntity,
vel: Vec3<f32>,
},
}
pub enum ServerEvent {
@ -21,6 +31,8 @@ pub enum ServerEvent {
},
Respawn(EcsEntity),
Shoot(EcsEntity),
Mount(EcsEntity, EcsEntity),
Unmount(EcsEntity),
}
pub struct EventBus<E> {

@ -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<T> for EcsCompPhantom
@ -44,6 +46,8 @@ sphynx::sum_type! {
LightEmitter(PhantomData<comp::LightEmitter>),
Item(PhantomData<comp::Item>),
Scale(PhantomData<comp::Scale>),
MountState(PhantomData<comp::MountState>),
Mounting(PhantomData<comp::Mounting>),
}
}
impl sphynx::CompPacket for EcsCompPacket {

@ -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::<comp::LightEmitter>();
ecs.register_synced::<comp::Item>();
ecs.register_synced::<comp::Scale>();
ecs.register_synced::<comp::Mounting>();
ecs.register_synced::<comp::MountState>();
// Register components send from clients -> server
ecs.register::<comp::Controller>();
@ -145,7 +148,7 @@ impl State {
ecs.register::<comp::Admin>();
// 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<C: Component>(&mut self, entity: EcsEntity) -> Option<C> {
self.ecs.write_storage().remove(entity)
}
/// Read a component attributed to a particular entity.
pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
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::<DeltaTime>().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::<comp::MountState>(),
)
.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::<comp::Controller>()
.get(mounter)
.cloned()
.map(|x| (x, mounter))
})
{
let pos = self.ecs.read_storage::<comp::Pos>().get(entity).copied();
let ori = self.ecs.read_storage::<comp::Ori>().get(entity).copied();
let vel = self.ecs.read_storage::<comp::Vel>().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::<comp::Controller>()
.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::<comp::Mounting>(),
)
.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::<comp::Mounting>().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::<EventBus<LocalEvent>>().recv_all();
for event in events {
let mut velocities = self.ecs.write_storage::<comp::Vel>();
let mut controllers = self.ecs.write_storage::<comp::Controller>();
match event {
LocalEvent::LandOnGround { entity, vel } => {
if let Some(stats) = self.ecs.write_storage::<comp::Stats>().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::<f32>::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,

@ -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::<f32>() - 0.5, rand::random::<f32>() - 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::<f32>::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::<f32>::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()

@ -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
}
}

@ -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<ServerEvent>>,
Read<'a, EventBus<LocalEvent>>,
@ -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)),
}
}
}
}
}

@ -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]);
}

@ -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::<f32>::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::<f32>::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::<f32>::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::<f32>::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;
}
}
}
}

@ -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<f32>, grav: f32, damp: f32) -> Vec3<f3
// this is not linear damping, because it is proportional to the original
// velocity this "linear" damping in in fact, quite exponential. and thus
// must be interpolated accordingly
let linear_damp = if damp < 1.0 {
(1.0 - damp).powf(dt * 60.0)
} else {
0.0
};
let linear_damp = (1.0 - damp.min(1.0)).powf(dt * 60.0);
lv.z = (lv.z - grav * dt).max(-50.0);
lv * linear_damp
@ -53,6 +50,7 @@ impl<'a> 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<f32>, near_iter| {
let collision_with = |pos: Vec3<f32>, 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::<f32>::from(pos.0 - pos_other.0);

@ -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();
}

@ -363,6 +363,53 @@ impl Server {
.insert(entity, comp::ForceUpdate);
}
}
ServerEvent::Mount(mounter, mountee) => {
if state
.ecs()
.read_storage::<comp::Mounting>()
.get(mounter)
.is_none()
{
let not_mounting_yet = if let Some(comp::MountState::Unmounted) = state
.ecs()
.write_storage::<comp::MountState>()
.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::<comp::Mounting>()
.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::<comp::MountState>()
.get_mut(mountee_entity)
.map(|ms| *ms = comp::MountState::Unmounted);
}
state.delete_component::<comp::Mounting>(mounter);
}
}
if let Some(entity) = todo_remove {

@ -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();

@ -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();

@ -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();

@ -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();

@ -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<f32>, Vec3<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 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
}
}

@ -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();

@ -9,15 +9,19 @@ pub struct GlidingAnimation;
impl Animation for GlidingAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = (f32, f64);
type Dependency = (Vec3<f32>, Vec3<f32>, Vec3<f32>, 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::<f32>::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::<f32>::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

@ -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();

@ -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();

@ -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};

@ -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();

@ -10,21 +10,35 @@ pub struct RunAnimation;
impl Animation for RunAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = (f32, f32, f64);
type Dependency = (Vec3<f32>, Vec3<f32>, Vec3<f32>, 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::<f32>::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::<f32>::from(v).magnitude_squared())
.reduce_partial_min()
> 0.001
let tilt = if Vec2::new(ori, last_ori)
.map(|o| Vec2::<f32>::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

@ -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
}
}

@ -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);

@ -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::<f32>::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
}
}

@ -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();

@ -178,6 +178,7 @@ pub trait Animation {
skeleton: &Self::Skeleton,
dependency: Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &SkeletonAttr,
) -> Self::Skeleton;
}

@ -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();

@ -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();

@ -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();

@ -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();

@ -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();

@ -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();

@ -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",

@ -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();

@ -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,
);
}

@ -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<S: Skeleton> {
bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>,
last_movement_change: Instant,
last_action_change: Instant,
movement_time: f64,
action_time: f64,
skeleton: S,
pos: Vec3<f32>,
ori: Vec3<f32>,
last_ori: Vec3<f32>,
}
impl<S: Skeleton> FigureState<S> {
@ -440,11 +506,12 @@ impl<S: Skeleton> FigureState<S> {
.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<S: Skeleton> FigureState<S> {
scale: f32,
col: Rgba<f32>,
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<S: Skeleton> FigureState<S> {
self.ori = ori;
}
self.movement_time += (dt * movement_rate) as f64;
self.action_time += (dt * action_rate) as f64;
let mat = Mat4::<f32>::identity()
* Mat4::translation_3d(self.pos)
* Mat4::rotation_z(-ori.x.atan2(ori.y))

@ -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<f32> = 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::<comp::Pos>()
.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::<comp::Pos>(),
&client.state().ecs().read_storage::<comp::MountState>(),
)
.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();

@ -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),

@ -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);