diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index b21d90008f..86db71191b 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -19,6 +19,7 @@ pub struct StateUpdate { pub density: Density, pub energy: Energy, pub swap_equipped_weapons: bool, + pub should_strafe: bool, pub queued_inputs: BTreeMap, pub removed_inputs: Vec, pub local_events: VecDeque, @@ -34,6 +35,7 @@ impl From<&JoinData<'_>> for StateUpdate { density: *data.density, energy: *data.energy, swap_equipped_weapons: false, + should_strafe: data.inputs.strafing, character: data.character.clone(), queued_inputs: BTreeMap::new(), removed_inputs: Vec::new(), diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 63c3664f8d..2979de12a5 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -199,6 +199,10 @@ pub struct ControllerInputs { * limits) */ pub look_dir: Dir, pub select_pos: Option>, + /// Attempt to enable strafing. + /// Currently, setting this to false will *not* disable strafing during a + /// wielding character state. + pub strafing: bool, } #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index eefa5e1a15..876d049618 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -307,7 +307,10 @@ pub fn handle_forced_movement(data: &JoinData, update: &mut StateUpdate, movemen } pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { - let strafe_aim = update.character.is_aimed() && data.body.can_strafe(); + // TODO: Don't always check `character.is_aimed()`, allow the frontend to + // control whether the player strafes during an aimed `CharacterState`. + let strafe_aim = + (update.character.is_aimed() || update.should_strafe) && data.body.can_strafe(); if let Some(dir) = (strafe_aim || update.character.is_attack()) .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) diff --git a/voxygen/anim/src/character/stand.rs b/voxygen/anim/src/character/stand.rs index f1829e666e..50ddea63a7 100644 --- a/voxygen/anim/src/character/stand.rs +++ b/voxygen/anim/src/character/stand.rs @@ -13,6 +13,8 @@ impl Animation for StandAnimation { Option, Option, (Option, Option), + Vec3, + Vec3, f32, Vec3, ); @@ -24,7 +26,7 @@ impl Animation for StandAnimation { #[cfg_attr(feature = "be-dyn-lib", export_name = "character_stand")] fn update_skeleton_inner<'a>( skeleton: &Self::Skeleton, - (active_tool_kind, second_tool_kind, hands, global_time, avg_vel): Self::Dependency<'a>, + (active_tool_kind, second_tool_kind, hands, orientation, last_ori, global_time, avg_vel): Self::Dependency<'a>, anim_time: f32, _rate: &mut f32, s_a: &SkeletonAttr, @@ -33,6 +35,19 @@ impl Animation for StandAnimation { let slow = (anim_time * 1.0).sin(); let impact = (avg_vel.z).max(-15.0); + let ori: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(ori, last_ori) + .map(|o| 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.2) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 1.3; let head_look = Vec2::new( ((global_time + anim_time) / 10.0).floor().mul(7331.0).sin() * 0.15, ((global_time + anim_time) / 10.0).floor().mul(1337.0).sin() * 0.07, @@ -159,7 +174,7 @@ impl Animation for StandAnimation { next.lantern.position = Vec3::new(-0.5, -0.5, -2.5); next.lantern.orientation = next.hand_r.orientation.inverse() * Quaternion::rotation_x(fast * 0.1) - * Quaternion::rotation_y(fast2 * 0.1); + * Quaternion::rotation_y(fast2 * 0.1 + tilt * 3.0); } next.torso.position = Vec3::new(0.0, 0.0, 0.0) * s_a.scaler; diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index d4aa8aa752..807b9ff0fa 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -6,7 +6,7 @@ use treeculler::Frustum; use vek::*; pub const NEAR_PLANE: f32 = 0.0625; -pub const FAR_PLANE: f32 = 524288.06; // excessive precision: 524288.0625 +pub const FAR_PLANE: f32 = 524288.06; // excessive precision: 524288.0625 const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; @@ -415,8 +415,8 @@ impl Camera { self.tgt_ori.x = (self.tgt_ori.x + delta.x).rem_euclid(2.0 * PI); // Clamp camera pitch to the vertical limits self.tgt_ori.y = (self.tgt_ori.y + delta.y) - .min(PI / 2.0 - 0.0001) - .max(-PI / 2.0 + 0.0001); + .min(PI / 2.0 - 0.001) + .max(-PI / 2.0 + 0.001); // Wrap camera roll self.tgt_ori.z = (self.tgt_ori.z + delta.z).rem_euclid(2.0 * PI); } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index c43d262185..5174bb0330 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -807,7 +807,16 @@ impl FigureMgr { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( &CharacterSkeleton::new(holding_lantern), - (active_tool_kind, second_tool_kind, hands, time, rel_avg_vel), + ( + active_tool_kind, + second_tool_kind, + hands, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + time, + rel_avg_vel, + ), state.state_time, &mut state_animation_rate, skeleton_attr, diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 2e7464f658..5792cb46e4 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -846,6 +846,10 @@ impl PlayState for SessionState { Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap(); } } + self.inputs.strafing = matches!( + self.scene.camera().get_mode(), + camera::CameraMode::FirstPerson + ); // Get the current state of movement related inputs let input_vec = self.key_state.dir_vec();