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 980bbbb711
commit 3f2e22f039
59 changed files with 1208 additions and 156 deletions

7
Cargo.lock generated
View File

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

BIN
assets/voxygen/element/skillbar/stamina_wheel-0.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-1.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-2.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-3.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-4.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-5.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-6.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-7.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skillbar/stamina_wheel-empty.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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