diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 8f6c95c028..c3801b7889 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -53,6 +53,15 @@ pub enum CharacterState { } impl CharacterState { + pub fn is_wielded(&self) -> bool { + match self { + CharacterState::Wielded(_) => true, + CharacterState::BasicAttack(_) => true, + CharacterState::BasicBlock(_) => true, + _ => false, + } + } + pub fn is_attack(&self) -> bool { match self { CharacterState::BasicAttack(_) => true, diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index e53fbe5a48..0c74f20304 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -16,6 +16,7 @@ pub enum EnergySource { Regen, Revive, Climb, + Roll, Unknown, } diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 57496dda0f..cd62d556e5 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -24,6 +24,7 @@ sum_type! { AbilityAction(comp::AbilityAction), AbilityPool(comp::AbilityPool), Attacking(comp::Attacking), + CharacterState(comp::CharacterState), } } // Automatically derive From<T> for EcsCompPhantom @@ -47,6 +48,7 @@ sum_type! { AbilityAction(PhantomData<comp::AbilityAction>), AbilityPool(PhantomData<comp::AbilityPool>), Attacking(PhantomData<comp::Attacking>), + CharacterState(PhantomData<comp::CharacterState>), } } impl sync::CompPacket for EcsCompPacket { @@ -70,6 +72,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::AbilityAction(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::AbilityPool(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Attacking(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world), } } @@ -91,6 +94,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::AbilityAction(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::AbilityPool(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Attacking(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world), } } @@ -118,6 +122,9 @@ impl sync::CompPacket for EcsCompPacket { sync::handle_remove::<comp::AbilityPool>(entity, world) }, EcsCompPhantom::Attacking(_) => sync::handle_remove::<comp::Attacking>(entity, world), + EcsCompPhantom::CharacterState(_) => { + sync::handle_remove::<comp::CharacterState>(entity, world) + }, } } } diff --git a/common/src/states/basic_attack.rs b/common/src/states/basic_attack.rs index d633d988d8..f142bc7e13 100644 --- a/common/src/states/basic_attack.rs +++ b/common/src/states/basic_attack.rs @@ -1,6 +1,6 @@ use crate::{ comp::{Attacking, CharacterState, EcsStateData, ItemKind::Tool, StateUpdate, ToolData}, - states::{utils, StateHandler}, + states::StateHandler, }; use std::{collections::VecDeque, time::Duration}; diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index fb91ce76a0..18d7dd1ce2 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{EcsStateData, StateUpdate}, + comp::{CharacterState, EcsStateData, StateUpdate}, states::StateHandler, }; use std::{collections::VecDeque, time::Duration}; @@ -26,6 +26,12 @@ impl StateHandler for State { server_events: VecDeque::new(), }; + handle_move_dir(&ecs_data, &mut update); + + if !ecs_data.physics.on_ground || !ecs_data.inputs.secondary.is_pressed() { + update.character = CharacterState::Wielded(None); + } + update } } diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 14223b0e10..be3944ba13 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -50,6 +50,16 @@ impl StateHandler for State { .unwrap_or_default() * ROLL_SPEED; + // Smooth orientation + if update.vel.0.magnitude_squared() > 0.0001 + && (update.ori.0.normalized() - Vec3::from(update.vel.0).normalized()) + .magnitude_squared() + > 0.001 + { + update.ori.0 = + vek::ops::Slerp::slerp(update.ori.0, update.vel.0.into(), 9.0 * ecs_data.dt.0); + } + if self.remaining_duration == Duration::default() { // Roll duration has expired update.character = CharacterState::Idle(None); diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 5527365e20..d6b8d7338f 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{Attacking, CharacterState, EcsStateData, ItemKind::Tool, StateUpdate}, + comp::{Attacking, CharacterState, EcsStateData, EnergySource, ItemKind::Tool, StateUpdate}, event::LocalEvent, }; use std::time::Duration; @@ -27,7 +27,10 @@ pub fn handle_move_dir(ecs_data: &EcsStateData, update: &mut StateUpdate) { } // Set direction based on move direction - let ori_dir = if update.character.is_attack() || update.character.is_block() { + let ori_dir = if update.character.is_wielded() + || update.character.is_attack() + || update.character.is_block() + { Vec2::from(ecs_data.inputs.look_dir).normalized() } else { Vec2::from(update.vel.0) @@ -58,7 +61,7 @@ pub fn handle_sit(ecs_data: &EcsStateData, update: &mut StateUpdate) { } pub fn handle_climb(ecs_data: &EcsStateData, update: &mut StateUpdate) { - if (ecs_data.inputs.climb.is_just_pressed() || ecs_data.inputs.climb_down.is_pressed()) + if (ecs_data.inputs.climb.is_pressed() || ecs_data.inputs.climb_down.is_pressed()) && ecs_data.physics.on_wall.is_some() && !ecs_data.physics.on_ground //&& update.vel.0.z < 0.0 @@ -121,6 +124,10 @@ pub fn handle_dodge(ecs_data: &EcsStateData, update: &mut StateUpdate) { if ecs_data.inputs.roll.is_pressed() && ecs_data.physics.on_ground && ecs_data.body.is_humanoid() + && update + .energy + .try_change_by(-200, EnergySource::Roll) + .is_ok() { update.character = state; } diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index 19e8182c09..548b636bda 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -5,7 +5,7 @@ use crate::{ }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; -const ENERGY_REGEN_ACCEL: f32 = 20.0; +const ENERGY_REGEN_ACCEL: f32 = 10.0; /// This system kills players, levels them up, and regenerates energy. pub struct Sys; @@ -87,15 +87,17 @@ impl<'a> System<'a> for Sys { as i32, EnergySource::Regen, ); - energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0; + energy.regen_rate = + (energy.regen_rate + ENERGY_REGEN_ACCEL * dt.0).min(100.0); } }, // All other states do not regen and set the rate back to zero. - _ => { + CharacterState::Wielded(_) => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } }, + _ => {}, } } } diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 9363dcfc59..f48f8a9f59 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -1,8 +1,8 @@ use super::SysTimer; use common::{ comp::{ - AbilityPool, Body, CanBuild, Energy, Gravity, Item, LightEmitter, Mass, MountState, - Mounting, Player, Scale, Stats, Sticky, + AbilityPool, Body, CanBuild, CharacterState, Energy, Gravity, Item, LightEmitter, Mass, + MountState, Mounting, Player, Scale, Stats, Sticky, }, msg::EcsCompPacket, sync::{EntityPackage, SyncPackage, Uid, UpdateTracker, WorldSyncExt}, @@ -52,6 +52,7 @@ pub struct TrackedComps<'a> { pub sticky: ReadStorage<'a, Sticky>, pub gravity: ReadStorage<'a, Gravity>, pub ability_pool: ReadStorage<'a, AbilityPool>, + pub character_state: ReadStorage<'a, CharacterState>, } impl<'a> TrackedComps<'a> { pub fn create_entity_package(&self, entity: EcsEntity) -> EntityPackage<EcsCompPacket> { @@ -109,6 +110,10 @@ impl<'a> TrackedComps<'a> { .get(entity) .copied() .map(|c| comps.push(c.into())); + self.character_state + .get(entity) + .copied() + .map(|c| comps.push(c.into())); EntityPackage { uid, comps } } @@ -130,6 +135,7 @@ pub struct ReadTrackers<'a> { pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>, pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>, pub ability_pool: ReadExpect<'a, UpdateTracker<AbilityPool>>, + pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>, } impl<'a> ReadTrackers<'a> { pub fn create_sync_package( @@ -158,6 +164,12 @@ impl<'a> ReadTrackers<'a> { .with_component(&comps.uid, &*self.sticky, &comps.sticky, filter) .with_component(&comps.uid, &*self.gravity, &comps.gravity, filter) .with_component(&comps.uid, &*self.ability_pool, &comps.ability_pool, filter) + .with_component( + &comps.uid, + &*self.character_state, + &comps.character_state, + filter, + ) } } @@ -178,6 +190,7 @@ pub struct WriteTrackers<'a> { sticky: WriteExpect<'a, UpdateTracker<Sticky>>, gravity: WriteExpect<'a, UpdateTracker<Gravity>>, ability_pool: WriteExpect<'a, UpdateTracker<AbilityPool>>, + character_state: WriteExpect<'a, UpdateTracker<CharacterState>>, } fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { @@ -197,6 +210,9 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { trackers.sticky.record_changes(&comps.sticky); trackers.gravity.record_changes(&comps.gravity); trackers.ability_pool.record_changes(&comps.ability_pool); + trackers + .character_state + .record_changes(&comps.character_state); } pub fn register_trackers(world: &mut World) { @@ -215,6 +231,7 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::<Sticky>(); world.register_tracker::<Gravity>(); world.register_tracker::<AbilityPool>(); + world.register_tracker::<CharacterState>(); } /// Deleted entities grouped by region diff --git a/voxygen/src/audio/sfx/event_mapper/movement.rs b/voxygen/src/audio/sfx/event_mapper/movement.rs index 559e036aa4..00e80b6815 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement.rs @@ -124,28 +124,23 @@ impl MovementEventMapper { } } - /// Voxygen has an existing list of character states via `MoveState::*` and - /// `ActionState::*` however that list does not provide enough - /// resolution to target specific entity events, such as opening or - /// closing the glider. These methods translate those entity states with - /// some additional data into more specific `SfxEvent`'s which we attach - /// sounds to + /// Voxygen has an existing list of character states however that list does + /// not provide enough resolution to target specific entity events, such + /// as opening or closing the glider. These methods translate those + /// entity states with some additional data into more specific + /// `SfxEvent`'s which we attach sounds to fn map_movement_event(current_event: &CharacterState, previous_event: SfxEvent) -> SfxEvent { - match (current_event, previous_event) { - (CharacterState::Roll(_), _) => SfxEvent::Roll, - (CharacterState::Climb(_), _) => SfxEvent::Climb, - (CharacterState::Idle(_), _) => SfxEvent::Run, - (CharacterState::Idle(_), SfxEvent::Glide) => SfxEvent::GliderClose, - (CharacterState::Idle(_), SfxEvent::Fall) => SfxEvent::Run, - (CharacterState::Idle(_), SfxEvent::Jump) => SfxEvent::Idle, - (CharacterState::Glide(_), previous_event) => { + match (previous_event, current_event) { + (_, CharacterState::Roll(_)) => SfxEvent::Roll, + (_, CharacterState::Climb(_)) => SfxEvent::Climb, + (SfxEvent::Glide, CharacterState::Idle(_)) => SfxEvent::GliderClose, + (previous_event, CharacterState::Glide(_)) => { if previous_event != SfxEvent::GliderOpen && previous_event != SfxEvent::Glide { SfxEvent::GliderOpen } else { SfxEvent::Glide } }, - (CharacterState::Idle(_), SfxEvent::Glide) => SfxEvent::GliderClose, _ => SfxEvent::Idle, } }