diff --git a/CHANGELOG.md b/CHANGELOG.md index b92f921d69..d3b43b63d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Sailing boat (currently requires spawning in) - Added a filter search function for crafting menu, use "input:_____" to search for recipe inputs - Added catalan (Catalonia) language translation +- Sneaking with weapons drawn ### Changed diff --git a/client/src/lib.rs b/client/src/lib.rs index 17eb989464..bdbcf94da0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -27,8 +27,9 @@ use common::{ invite::{InviteKind, InviteResponse}, skills::Skill, slot::Slot, - ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputKind, - InventoryAction, InventoryEvent, InventoryUpdateEvent, UtteranceKind, + CharacterState, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, + GroupManip, InputKind, InventoryAction, InventoryEvent, InventoryUpdateEvent, + UtteranceKind, }, event::{EventBus, LocalEvent}, grid::Grid, @@ -300,9 +301,7 @@ impl Client { // Initialize `State` let mut state = State::client(); // Client-only components - state - .ecs_mut() - .register::>(); + state.ecs_mut().register::>(); let entity = state.ecs_mut().apply_entity_package(entity_package); *state.ecs_mut().write_resource() = time_of_day; @@ -912,8 +911,8 @@ impl Client { pub fn is_dead(&self) -> bool { self.current::().map_or(false, |h| h.is_dead) } pub fn is_gliding(&self) -> bool { - self.current::() - .map_or(false, |cs| matches!(cs, comp::CharacterState::Glide(_))) + self.current::() + .map_or(false, |cs| matches!(cs, CharacterState::Glide(_))) } pub fn split_swap_slots(&mut self, a: comp::slot::Slot, b: comp::slot::Slot) { @@ -1160,7 +1159,7 @@ impl Client { pub fn is_wielding(&self) -> Option { self.state .ecs() - .read_storage::() + .read_storage::() .get(self.entity()) .map(|cs| cs.is_wield()) } @@ -1177,9 +1176,9 @@ impl Client { let is_sitting = self .state .ecs() - .read_storage::() + .read_storage::() .get(self.entity()) - .map(|cs| matches!(cs, comp::CharacterState::Sit)); + .map(|cs| matches!(cs, CharacterState::Sit)); match is_sitting { Some(true) => self.control_action(ControlAction::Stand), @@ -1192,9 +1191,9 @@ impl Client { let is_dancing = self .state .ecs() - .read_storage::() + .read_storage::() .get(self.entity()) - .map(|cs| matches!(cs, comp::CharacterState::Dance)); + .map(|cs| matches!(cs, CharacterState::Dance)); match is_dancing { Some(true) => self.control_action(ControlAction::Stand), @@ -1211,9 +1210,9 @@ impl Client { let is_sneaking = self .state .ecs() - .read_storage::() + .read_storage::() .get(self.entity()) - .map(|cs| matches!(cs, comp::CharacterState::Sneak)); + .map(CharacterState::is_stealthy); match is_sneaking { Some(true) => self.control_action(ControlAction::Stand), @@ -1226,14 +1225,9 @@ impl Client { let using_glider = self .state .ecs() - .read_storage::() + .read_storage::() .get(self.entity()) - .map(|cs| { - matches!( - cs, - comp::CharacterState::GlideWield(_) | comp::CharacterState::Glide(_) - ) - }); + .map(|cs| matches!(cs, CharacterState::GlideWield(_) | CharacterState::Glide(_))); match using_glider { Some(true) => self.control_action(ControlAction::Unwield), @@ -1439,11 +1433,11 @@ impl Client { { prof_span!("Last comps update"); let ecs = self.state.ecs(); - let mut last_character_states = ecs.write_storage::>(); + let mut last_character_states = ecs.write_storage::>(); for (entity, _, character_state) in ( &ecs.entities(), &ecs.read_storage::(), - &ecs.read_storage::(), + &ecs.read_storage::(), ) .join() { diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index b25f345d86..b6eb61fe98 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1634,7 +1634,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { timer: Duration::default(), stage_section: StageSection::Buildup, was_wielded: false, // false by default. utils might set it to true - was_sneak: false, + is_sneaking: false, was_combo: None, }), CharacterAbility::ComboMelee { diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index d34b4624e3..46350e7cca 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -66,12 +66,11 @@ impl From<&JoinData<'_>> for StateUpdate { } #[derive(Clone, Debug, Display, PartialEq, Serialize, Deserialize)] pub enum CharacterState { - Idle, + Idle(idle::Data), Climb(climb::Data), Sit, Dance, Talk, - Sneak, Glide(glide::Data), GlideWield(glide_wield::Data), /// A stunned state @@ -81,7 +80,7 @@ pub enum CharacterState { /// Player is busy equipping or unequipping weapons Equipping(equipping::Data), /// Player is holding a weapon and can perform other actions - Wielding, + Wielding(wielding::Data), /// A dodge where player can roll Roll(roll::Data), /// A basic melee attack (e.g. sword) @@ -131,7 +130,7 @@ impl CharacterState { pub fn is_wield(&self) -> bool { matches!( self, - CharacterState::Wielding + CharacterState::Wielding(_) | CharacterState::BasicMelee(_) | CharacterState::BasicRanged(_) | CharacterState::DashMelee(_) @@ -153,7 +152,18 @@ impl CharacterState { } pub fn is_stealthy(&self) -> bool { - matches!(self, CharacterState::Sneak | CharacterState::Roll(_)) + matches!( + self, + CharacterState::Idle(idle::Data { is_sneaking: true }) + | CharacterState::Wielding(wielding::Data { + is_sneaking: true, + .. + }) + | CharacterState::Roll(roll::Data { + is_sneaking: true, + .. + }) + ) } pub fn is_attack(&self) -> bool { @@ -194,7 +204,7 @@ impl CharacterState { | CharacterState::BasicBeam(_) | CharacterState::Stunned(_) | CharacterState::UseItem(_) - | CharacterState::Wielding + | CharacterState::Wielding(_) | CharacterState::Talk ) } @@ -258,7 +268,7 @@ impl CharacterState { pub fn behavior(&self, j: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { match &self { - CharacterState::Idle => states::idle::Data.behavior(j, output_events), + CharacterState::Idle(data) => data.behavior(j, output_events), CharacterState::Talk => states::talk::Data.behavior(j, output_events), CharacterState::Climb(data) => data.behavior(j, output_events), CharacterState::Glide(data) => data.behavior(j, output_events), @@ -270,12 +280,9 @@ impl CharacterState { CharacterState::Dance => { states::dance::Data::behavior(&states::dance::Data, j, output_events) }, - CharacterState::Sneak => { - states::sneak::Data::behavior(&states::sneak::Data, j, output_events) - }, CharacterState::BasicBlock(data) => data.behavior(j, output_events), CharacterState::Roll(data) => data.behavior(j, output_events), - CharacterState::Wielding => states::wielding::Data.behavior(j, output_events), + CharacterState::Wielding(data) => data.behavior(j, output_events), CharacterState::Equipping(data) => data.behavior(j, output_events), CharacterState::ComboMelee(data) => data.behavior(j, output_events), CharacterState::BasicMelee(data) => data.behavior(j, output_events), @@ -306,7 +313,7 @@ impl CharacterState { action: ControlAction, ) -> StateUpdate { match &self { - CharacterState::Idle => states::idle::Data.handle_event(j, output_events, action), + CharacterState::Idle(data) => data.handle_event(j, output_events, action), CharacterState::Talk => states::talk::Data.handle_event(j, output_events, action), CharacterState::Climb(data) => data.handle_event(j, output_events, action), CharacterState::Glide(data) => data.handle_event(j, output_events, action), @@ -318,14 +325,9 @@ impl CharacterState { CharacterState::Dance => { states::dance::Data::handle_event(&states::dance::Data, j, output_events, action) }, - CharacterState::Sneak => { - states::sneak::Data::handle_event(&states::sneak::Data, j, output_events, action) - }, CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action), CharacterState::Roll(data) => data.handle_event(j, output_events, action), - CharacterState::Wielding => { - states::wielding::Data.handle_event(j, output_events, action) - }, + CharacterState::Wielding(data) => data.handle_event(j, output_events, action), CharacterState::Equipping(data) => data.handle_event(j, output_events, action), CharacterState::ComboMelee(data) => data.handle_event(j, output_events, action), CharacterState::BasicMelee(data) => data.handle_event(j, output_events, action), @@ -351,7 +353,7 @@ impl CharacterState { } impl Default for CharacterState { - fn default() -> Self { Self::Idle } + fn default() -> Self { Self::Idle(idle::Data { is_sneaking: false }) } } impl Component for CharacterState { diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs index 2ee79957b7..d15733c684 100644 --- a/common/src/states/basic_aura.rs +++ b/common/src/states/basic_aura.rs @@ -9,6 +9,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -130,12 +131,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index ffc7b4ad2e..620f77f062 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -11,6 +11,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, terrain::Block, uid::Uid, @@ -204,14 +205,15 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index f0719ba15e..2b048405fe 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -1,7 +1,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, InputKind, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + wielding, + }, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -83,12 +86,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 314bd4e1f7..77be4a809a 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -7,6 +7,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -163,13 +164,14 @@ impl CharacterBehavior for Data { if input_is_pressed(data, self.static_data.ability_info.input) { reset_state(self, data, output_events, &mut update); } else { - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 2e936d74f3..cc08ad9f9f 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -7,6 +7,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, util::Dir, }; @@ -126,13 +127,14 @@ impl CharacterBehavior for Data { if input_is_pressed(data, self.static_data.ability_info.input) { reset_state(self, data, output_events, &mut update); } else { - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index cec7765b68..f5f9768a2f 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -11,6 +11,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, terrain::Block, vol::ReadVol, @@ -231,12 +232,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/blink.rs b/common/src/states/blink.rs index 24623e0673..7d5f5c1b8a 100644 --- a/common/src/states/blink.rs +++ b/common/src/states/blink.rs @@ -4,6 +4,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -80,12 +81,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/boost.rs b/common/src/states/boost.rs index 4e1ca96fac..ec45f71b8b 100644 --- a/common/src/states/boost.rs +++ b/common/src/states/boost.rs @@ -3,6 +3,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -55,7 +56,7 @@ impl CharacterBehavior for Data { .0 .magnitude() .min(self.static_data.max_exit_velocity); - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); } } diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index e4bc23107f..a73bb26c3e 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -6,6 +6,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, + wielding, }, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, }; @@ -220,14 +221,15 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 9f5ca30b82..ac94a19035 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -7,6 +7,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -172,12 +173,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 4857e40242..c20875aa3f 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -8,6 +8,7 @@ use crate::{ event::LocalEvent, states::{ behavior::{CharacterBehavior, JoinData}, + idle, utils::*, }, util::Dir, @@ -78,7 +79,7 @@ impl CharacterBehavior for Data { CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0, )); }; - update.character = CharacterState::Idle {}; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); return update; }; // Move player @@ -102,7 +103,7 @@ impl CharacterBehavior for Data { .try_change_by(-energy_use * data.dt.0) .is_err() { - update.character = CharacterState::Idle {}; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); } // Set orientation direction based on wall direction diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 17efb030b6..a1fd2fc353 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -8,6 +8,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, }; @@ -343,13 +344,14 @@ impl CharacterBehavior for Data { if input_is_pressed(data, self.static_data.ability_info.input) { reset_state(self, data, output_events, &mut update); } else { - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/dance.rs b/common/src/states/dance.rs index ccaf6a4c39..0dc8653c3c 100644 --- a/common/src/states/dance.rs +++ b/common/src/states/dance.rs @@ -1,7 +1,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, + }, }; use serde::{Deserialize, Serialize}; @@ -17,7 +20,7 @@ impl CharacterBehavior for Data { // Try to Fall/Stand up/Move if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); } update @@ -49,7 +52,7 @@ impl CharacterBehavior for Data { fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // Try to Fall/Stand up/Move - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); update } } diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index d009305cb8..e311f911de 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -4,6 +4,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, }; @@ -339,14 +340,15 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index 78061755c6..b70cb72ade 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -1,7 +1,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + wielding, + }, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -20,6 +23,7 @@ pub struct Data { pub static_data: StaticData, /// Timer for each stage pub timer: Duration, + pub is_sneaking: bool, } impl CharacterBehavior for Data { @@ -27,7 +31,7 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); - handle_move(data, &mut update, 1.0); + handle_move(data, &mut update, if self.is_sneaking { 0.4 } else { 1.0 }); handle_jump(data, output_events, &mut update, 1.0); if self.timer < self.static_data.buildup_duration { @@ -38,7 +42,9 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { + is_sneaking: self.is_sneaking, + }); } update diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 1c66e519cf..5641c66a2e 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -6,7 +6,7 @@ use crate::{ }, states::{ behavior::{CharacterBehavior, JoinData}, - glide_wield, + glide_wield, idle, }, util::{Dir, Plane, Projection}, }; @@ -87,7 +87,7 @@ impl CharacterBehavior for Data { .and_then(|inv| inv.equipped(EquipSlot::Glider)) .is_none() { - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); } else if !handle_climb(data, &mut update) { let air_flow = data .physics @@ -201,7 +201,7 @@ impl CharacterBehavior for Data { fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); update } } diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 961c9bb2aa..1fbc49278e 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -6,7 +6,7 @@ use crate::{ }, states::{ behavior::{CharacterBehavior, JoinData}, - glide, + glide, idle, }, }; use serde::{Deserialize, Serialize}; @@ -72,7 +72,7 @@ impl CharacterBehavior for Data { ..*self }) } else { - CharacterState::Idle + CharacterState::Idle(idle::Data { is_sneaking: false }) }; } @@ -92,7 +92,7 @@ impl CharacterBehavior for Data { fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); update } diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index 43b95c996c..5359e0f6de 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -1,22 +1,33 @@ use super::utils::*; use crate::{ - comp::{character_state::OutputEvents, InventoryAction, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; +use serde::{Deserialize, Serialize}; -pub struct Data; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + pub is_sneaking: bool, +} impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); - handle_move(data, &mut update, 1.0); + handle_move(data, &mut update, if self.is_sneaking { 0.4 } else { 1.0 }); handle_jump(data, output_events, &mut update, 1.0); handle_wield(data, &mut update); handle_climb(data, &mut update); handle_dodge_input(data, &mut update); + // Try to Fall/Stand up/Move + if self.is_sneaking + && (data.physics.on_ground.is_none() || data.physics.in_liquid().is_some()) + { + update.character = CharacterState::Idle(Data { is_sneaking: false }); + } + update } @@ -63,7 +74,7 @@ impl CharacterBehavior for Data { fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - attempt_sneak(data, &mut update); + update.character = CharacterState::Idle(Data { is_sneaking: true }); update } @@ -72,4 +83,10 @@ impl CharacterBehavior for Data { attempt_talk(data, &mut update); update } + + fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { + let mut update = StateUpdate::from(data); + update.character = CharacterState::Idle(Data { is_sneaking: false }); + update + } } diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 2c0b773cb2..0a8479f2fc 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -4,6 +4,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, + wielding, }, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, }; @@ -195,14 +196,15 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 608f815e6e..f64af5da0d 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -23,7 +23,6 @@ pub mod roll; pub mod self_buff; pub mod shockwave; pub mod sit; -pub mod sneak; pub mod spin_melee; pub mod sprite_interact; pub mod sprite_summon; diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index 180599a00f..131f4f5ed6 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -7,6 +7,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -148,12 +149,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index d87d599a41..dbd96c527f 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -7,7 +7,9 @@ use crate::{ event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, + idle, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -41,8 +43,8 @@ pub struct Data { pub stage_section: StageSection, /// Had weapon pub was_wielded: bool, - /// Was sneaking - pub was_sneak: bool, + /// Is sneaking, true if previous state was also considered sneaking + pub is_sneaking: bool, /// Was in state with combo pub was_combo: Option<(InputKind, u32)>, } @@ -128,20 +130,28 @@ impl CharacterBehavior for Data { c.stage = stage; } } else { - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { + is_sneaking: self.is_sneaking, + }); } - } else if self.was_wielded { - update.character = CharacterState::Wielding; - } else if self.was_sneak { - update.character = CharacterState::Sneak; } else { - update.character = CharacterState::Idle; + update.character = if self.was_wielded { + CharacterState::Wielding(wielding::Data { + is_sneaking: self.is_sneaking, + }) + } else { + CharacterState::Idle(idle::Data { + is_sneaking: self.is_sneaking, + }) + } } } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { + is_sneaking: self.is_sneaking, + }); }, } diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs index b8a3bd1d4a..836e132db6 100644 --- a/common/src/states/self_buff.rs +++ b/common/src/states/self_buff.rs @@ -8,6 +8,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -104,12 +105,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index d7ba8a66b2..3be0a48cec 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -8,6 +8,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -156,12 +157,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index cb8183f196..a3f59648dd 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -1,7 +1,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, + }, }; use serde::{Deserialize, Serialize}; @@ -17,7 +20,7 @@ impl CharacterBehavior for Data { // Try to Fall/Stand up/Move if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); } update @@ -49,7 +52,7 @@ impl CharacterBehavior for Data { fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // Try to Fall/Stand up/Move - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); update } } diff --git a/common/src/states/sneak.rs b/common/src/states/sneak.rs deleted file mode 100644 index d92f40f9fa..0000000000 --- a/common/src/states/sneak.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::utils::*; -use crate::{ - comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, -}; - -pub struct Data; - -impl CharacterBehavior for Data { - fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - - handle_orientation(data, &mut update, 1.0, None); - handle_move(data, &mut update, 0.4); - handle_jump(data, output_events, &mut update, 1.0); - handle_wield(data, &mut update); - handle_climb(data, &mut update); - handle_dodge_input(data, &mut update); - - // Try to Fall/Stand up/Move - if data.physics.on_ground.is_none() { - update.character = CharacterState::Idle; - } - - update - } - - fn swap_equipped_weapons(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - attempt_swap_equipped_weapons(data, &mut update); - update - } - - fn manipulate_loadout( - &self, - data: &JoinData, - output_events: &mut OutputEvents, - inv_action: InventoryAction, - ) -> StateUpdate { - let mut update = StateUpdate::from(data); - handle_manipulate_loadout(data, output_events, &mut update, inv_action); - update - } - - fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - attempt_wield(data, &mut update); - update - } - - fn glide_wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - attempt_glide_wield(data, &mut update); - update - } - - fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - attempt_sit(data, &mut update); - update - } - - fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - attempt_dance(data, &mut update); - update - } - - fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { - let mut update = StateUpdate::from(data); - update.character = CharacterState::Idle; - update - } -} diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 5ec0e2c88d..4b5115153c 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -8,6 +8,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, }; use serde::{Deserialize, Serialize}; @@ -206,14 +207,15 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); // Make sure attack component is removed data.updater.remove::(data.entity); }, diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index e825d519b8..82f48c98f0 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -2,7 +2,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, InventoryManip, StateUpdate}, event::ServerEvent, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, wielding, + }, terrain::SpriteKind, util::Dir, }; @@ -95,18 +98,21 @@ impl CharacterBehavior for Data { let inv_manip = InventoryManip::Collect(self.static_data.sprite_pos); output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip)); // Done - if self.static_data.was_wielded { - update.character = CharacterState::Wielding; - } else if self.static_data.was_sneak { - update.character = CharacterState::Sneak; + + update.character = if self.static_data.was_wielded { + CharacterState::Wielding(wielding::Data { + is_sneaking: self.static_data.was_sneak, + }) } else { - update.character = CharacterState::Idle; + CharacterState::Idle(idle::Data { + is_sneaking: self.static_data.was_sneak, + }) } } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); }, } diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index 2a81fe8f44..01f11a432d 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -5,6 +5,7 @@ use crate::{ states::{ behavior::{CharacterBehavior, JoinData}, utils::*, + wielding, }, terrain::{Block, SpriteKind}, vol::ReadVol, @@ -150,12 +151,13 @@ impl CharacterBehavior for Data { }); } else { // Done - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Wielding; + update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); }, } diff --git a/common/src/states/stunned.rs b/common/src/states/stunned.rs index 0641ce0ad4..e899895145 100644 --- a/common/src/states/stunned.rs +++ b/common/src/states/stunned.rs @@ -1,7 +1,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, PoiseState, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, wielding, + }, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -66,18 +69,20 @@ impl CharacterBehavior for Data { } else { // Done if self.was_wielded { - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } else { - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); } } }, _ => { // If it somehow ends up in an incorrect stage section if self.was_wielded { - update.character = CharacterState::Wielding; + update.character = + CharacterState::Wielding(wielding::Data { is_sneaking: false }); } else { - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); } }, } diff --git a/common/src/states/talk.rs b/common/src/states/talk.rs index b069e2dfd8..25a43a1e6f 100644 --- a/common/src/states/talk.rs +++ b/common/src/states/talk.rs @@ -1,7 +1,10 @@ use super::utils::*; use crate::{ comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate}, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, + }, }; use serde::{Deserialize, Serialize}; @@ -39,14 +42,14 @@ impl CharacterBehavior for Data { fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); attempt_sit(data, &mut update); update } fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); attempt_dance(data, &mut update); update } @@ -54,7 +57,7 @@ impl CharacterBehavior for Data { fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); // Try to Fall/Stand up/Move - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); update } } diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index db56d7f6a5..fcbeee7763 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -10,7 +10,10 @@ use crate::{ CharacterState, InventoryManip, StateUpdate, }, event::ServerEvent, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, wielding, + }, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -122,18 +125,20 @@ impl CharacterBehavior for Data { }); } else { // Done - if self.static_data.was_wielded { - update.character = CharacterState::Wielding; - } else if self.static_data.was_sneak { - update.character = CharacterState::Sneak; + update.character = if self.static_data.was_wielded { + CharacterState::Wielding(wielding::Data { + is_sneaking: self.static_data.was_sneak, + }) } else { - update.character = CharacterState::Idle; + CharacterState::Idle(idle::Data { + is_sneaking: self.static_data.was_sneak, + }) } } }, _ => { // If it somehow ends up in an incorrect stage section - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { is_sneaking: false }); }, } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index a296d51d63..da1ec5d8a8 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -557,17 +557,20 @@ pub fn attempt_wield(data: &JoinData<'_>, update: &mut StateUpdate) { }; // Moves entity into equipping state if there is some equip time, else moves - // intantly into wield + // instantly into wield if let Some(equip_time) = equip_time { update.character = CharacterState::Equipping(equipping::Data { static_data: equipping::StaticData { buildup_duration: equip_time, }, timer: Duration::default(), + is_sneaking: update.character.is_stealthy(), }); } else { - update.character = CharacterState::Wielding; - }; + update.character = CharacterState::Wielding(wielding::Data { + is_sneaking: update.character.is_stealthy(), + }); + } } /// Checks that player can `Sit` and updates `CharacterState` if so @@ -591,7 +594,7 @@ pub fn attempt_talk(data: &JoinData<'_>, update: &mut StateUpdate) { pub fn attempt_sneak(data: &JoinData<'_>, update: &mut StateUpdate) { if data.physics.on_ground.is_some() && data.body.is_humanoid() { - update.character = CharacterState::Sneak; + update.character = CharacterState::Idle(idle::Data { is_sneaking: true }); } } @@ -659,14 +662,14 @@ pub fn handle_manipulate_loadout( inv_slot, item_kind, item_definition_id: item.item_definition_id().to_string(), - was_wielded: matches!(data.character, CharacterState::Wielding), - was_sneak: matches!(data.character, CharacterState::Sneak), + was_wielded: matches!(data.character, CharacterState::Wielding(_)), + was_sneak: data.character.is_stealthy(), }, timer: Duration::default(), stage_section: StageSection::Buildup, }); } else { - // Else emit inventory action instantnaneously + // Else emit inventory action instantaneously output_events .emit_server(ServerEvent::InventoryManip(data.entity, inv_action.into())); } @@ -763,8 +766,8 @@ pub fn handle_manipulate_loadout( recover_duration, sprite_pos, sprite_kind: sprite_interact, - was_wielded: matches!(data.character, CharacterState::Wielding), - was_sneak: matches!(data.character, CharacterState::Sneak), + was_wielded: matches!(data.character, CharacterState::Wielding(_)), + was_sneak: data.character.is_stealthy(), }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -948,18 +951,17 @@ pub fn handle_dodge_input(data: &JoinData<'_>, update: &mut StateUpdate) { AbilityInfo::from_input(data, false, InputKind::Roll), data, )); - if let CharacterState::ComboMelee(c) = data.character { - if let CharacterState::Roll(roll) = &mut update.character { + if let CharacterState::Roll(roll) = &mut update.character { + if let CharacterState::ComboMelee(c) = data.character { roll.was_combo = Some((c.static_data.ability_info.input, c.stage)); roll.was_wielded = true; - } - } else if data.character.is_wield() { - if let CharacterState::Roll(roll) = &mut update.character { - roll.was_wielded = true; - } - } else if data.character.is_stealthy() { - if let CharacterState::Roll(roll) = &mut update.character { - roll.was_sneak = true; + } else { + if data.character.is_wield() { + roll.was_wielded = true; + } + if data.character.is_stealthy() { + roll.is_sneaking = true; + } } } } diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index 7c7fd20993..c49471bcaa 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -5,19 +5,33 @@ use crate::{ slot::{EquipSlot, Slot}, CharacterState, InventoryAction, StateUpdate, }, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + idle, + }, }; +use serde::{Deserialize, Serialize}; -pub struct Data; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + pub is_sneaking: bool, +} impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); - handle_move(data, &mut update, 1.0); + handle_move(data, &mut update, if self.is_sneaking { 0.4 } else { 1.0 }); handle_climb(data, &mut update); attempt_input(data, output_events, &mut update); + handle_jump(data, output_events, &mut update, 1.0); + + if self.is_sneaking + && (data.physics.on_ground.is_none() || data.physics.in_liquid().is_some()) + { + update.character = CharacterState::Wielding(Data { is_sneaking: false }); + } update } @@ -42,7 +56,9 @@ impl CharacterBehavior for Data { _, Slot::Equip(EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand), ) => { - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { + is_sneaking: self.is_sneaking, + }); }, _ => (), } @@ -58,7 +74,9 @@ impl CharacterBehavior for Data { fn unwield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - update.character = CharacterState::Idle; + update.character = CharacterState::Idle(idle::Data { + is_sneaking: self.is_sneaking, + }); update } @@ -76,7 +94,15 @@ impl CharacterBehavior for Data { fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - attempt_sneak(data, &mut update); + if data.physics.on_ground.is_some() && data.body.is_humanoid() { + update.character = CharacterState::Wielding(Data { is_sneaking: true }); + } + update + } + + fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { + let mut update = StateUpdate::from(data); + update.character = CharacterState::Wielding(Data { is_sneaking: false }); update } } diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index 7f0db06778..282523b561 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -13,7 +13,10 @@ use common::{ event::{EventBus, LocalEvent, ServerEvent}, outcome::Outcome, resources::DeltaTime, - states::behavior::{JoinData, JoinStruct}, + states::{ + behavior::{JoinData, JoinStruct}, + idle, + }, terrain::TerrainGrid, uid::Uid, }; @@ -269,7 +272,7 @@ impl<'a> System<'a> for Sys { // Mounted occurs after control actions have been handled // If mounted, character state is controlled by mount if let Some(Mounting(_)) = read_data.mountings.get(entity) { - let idle_state = CharacterState::Idle {}; + let idle_state = CharacterState::Idle(idle::Data { is_sneaking: false }); if *join_struct.char_state != idle_state { *join_struct.char_state = idle_state; } diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index 19841b5faf..b4bb53e536 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -194,7 +194,6 @@ impl<'a> System<'a> for Sys { | CharacterState::Talk { .. } | CharacterState::Sit { .. } | CharacterState::Dance { .. } - | CharacterState::Sneak { .. } | CharacterState::Glide { .. } | CharacterState::GlideWield { .. } | CharacterState::Wielding { .. } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 75469bd038..56cc85dbae 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1038,7 +1038,8 @@ pub fn handle_combo_change(server: &Server, entity: EcsEntity, change: i32) { pub fn handle_parry(server: &Server, entity: EcsEntity, energy_cost: f32) { let ecs = &server.state.ecs(); if let Some(mut character) = ecs.write_storage::().get_mut(entity) { - *character = CharacterState::Wielding; + *character = + CharacterState::Wielding(common::states::wielding::Data { is_sneaking: false }); }; if let Some(mut energy) = ecs.write_storage::().get_mut(entity) { energy.change_by(energy_cost); @@ -1082,7 +1083,7 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) { *char_state, CharacterState::SpriteInteract(_) | CharacterState::UseItem(_) ) { - *char_state = CharacterState::Idle; + *char_state = CharacterState::Idle(common::states::idle::Data { is_sneaking: false }); } } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 0703d826b8..061297bc80 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -940,7 +940,7 @@ impl<'a> AgentData<'a> { if thread_rng().gen_bool(0.1) && matches!( read_data.char_states.get(*self.entity), - Some(CharacterState::Wielding) + Some(CharacterState::Wielding(_)) ) { controller.actions.push(ControlAction::Unwield); @@ -988,7 +988,7 @@ impl<'a> AgentData<'a> { if thread_rng().gen_bool(0.1) && matches!( read_data.char_states.get(*self.entity), - Some(CharacterState::Wielding) + Some(CharacterState::Wielding(_)) ) { controller.actions.push(ControlAction::Unwield); @@ -2531,7 +2531,7 @@ impl<'a> AgentData<'a> { } else if matches!(self.char_state, CharacterState::Shockwave(_)) { agent.action_state.condition = false; } else if agent.action_state.condition - && matches!(self.char_state, CharacterState::Wielding) + && matches!(self.char_state, CharacterState::Wielding(_)) { controller .actions @@ -2552,7 +2552,7 @@ impl<'a> AgentData<'a> { && self.energy.current() > shockwave_cost { // if enemy is closing distance quickly, use shockwave to knock back - if matches!(self.char_state, CharacterState::Wielding) { + if matches!(self.char_state, CharacterState::Wielding(_)) { controller .actions .push(ControlAction::basic_input(InputKind::Ability(0))); @@ -3418,7 +3418,7 @@ impl<'a> AgentData<'a> { if matches!(self.char_state, CharacterState::Blink(_)) { *num_fireballs = rand::random::() % 4; } - } else if matches!(self.char_state, CharacterState::Wielding) { + } else if matches!(self.char_state, CharacterState::Wielding(_)) { *num_fireballs -= 1; controller.actions.push(ControlAction::StartInput { input: InputKind::Ability(1), diff --git a/voxygen/anim/src/character/mod.rs b/voxygen/anim/src/character/mod.rs index 94676c325b..d768e22488 100644 --- a/voxygen/anim/src/character/mod.rs +++ b/voxygen/anim/src/character/mod.rs @@ -22,6 +22,8 @@ pub mod shockwave; pub mod shoot; pub mod sit; pub mod sneak; +pub mod sneakequip; +pub mod sneakwield; pub mod spin; pub mod spinmelee; pub mod staggered; @@ -41,9 +43,10 @@ pub use self::{ jump::JumpAnimation, leapmelee::LeapAnimation, mount::MountAnimation, repeater::RepeaterAnimation, roll::RollAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, sit::SitAnimation, sneak::SneakAnimation, - spin::SpinAnimation, spinmelee::SpinMeleeAnimation, staggered::StaggeredAnimation, - stand::StandAnimation, stunned::StunnedAnimation, swim::SwimAnimation, - swimwield::SwimWieldAnimation, talk::TalkAnimation, wield::WieldAnimation, + sneakequip::SneakEquipAnimation, sneakwield::SneakWieldAnimation, spin::SpinAnimation, + spinmelee::SpinMeleeAnimation, staggered::StaggeredAnimation, stand::StandAnimation, + stunned::StunnedAnimation, swim::SwimAnimation, swimwield::SwimWieldAnimation, + talk::TalkAnimation, wield::WieldAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton}; use common::comp; diff --git a/voxygen/anim/src/character/sneakequip.rs b/voxygen/anim/src/character/sneakequip.rs new file mode 100644 index 0000000000..2f8baaa345 --- /dev/null +++ b/voxygen/anim/src/character/sneakequip.rs @@ -0,0 +1,198 @@ +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; +use common::comp::item::ToolKind; +use core::{f32::consts::PI, ops::Mul}; + +pub struct SneakEquipAnimation; + +impl Animation for SneakEquipAnimation { + type Dependency<'a> = (Option, Vec3, Vec3, Vec3, f32); + type Skeleton = CharacterSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"character_sneakequip\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "character_sneakequip")] + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (active_tool_kind, velocity, orientation, last_ori, global_time): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + let speed = Vec2::::from(velocity).magnitude(); + *rate = 1.0; + let slow = (anim_time * 3.0).sin(); + let breathe = ((anim_time * 0.5).sin()).abs(); + let walkintensity = if speed > 5.0 { 1.0 } else { 0.45 }; + let lower = if speed > 5.0 { 0.0 } else { 1.0 }; + let _snapfoot = if speed > 5.0 { 1.1 } else { 2.0 }; + let lab: f32 = 1.0; + let foothoril = (anim_time * 7.0 * lab + PI * 1.45).sin(); + let foothorir = (anim_time * 7.0 * lab + PI * (0.45)).sin(); + + let footvertl = (anim_time * 7.0 * lab).sin(); + let footvertr = (anim_time * 7.0 * lab + PI).sin(); + + let footrotl = ((5.0 / (2.5 + (2.5) * ((anim_time * 7.0 * lab + PI * 1.4).sin()).powi(2))) + .sqrt()) + * ((anim_time * 7.0 * lab + PI * 1.4).sin()); + + let footrotr = ((5.0 / (1.0 + (4.0) * ((anim_time * 7.0 * lab + PI * 0.4).sin()).powi(2))) + .sqrt()) + * ((anim_time * 7.0 * lab + PI * 0.4).sin()); + + let short = (anim_time * lab * 7.0).sin(); + let noisea = (anim_time * 11.0 + PI / 6.0).sin(); + let noiseb = (anim_time * 19.0 + PI / 4.0).sin(); + + let shorte = ((5.0 / (4.0 + 1.0 * ((anim_time * lab * 7.0).sin()).powi(2))).sqrt()) + * ((anim_time * lab * 7.0).sin()); + + let shortalt = (anim_time * lab * 7.0 + PI / 2.0).sin(); + + let head_look = Vec2::new( + (global_time + anim_time / 18.0).floor().mul(7331.0).sin() * 0.2, + (global_time + anim_time / 18.0).floor().mul(1337.0).sin() * 0.1, + ); + + let orientation: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(orientation, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && orientation.angle_between(last_ori).is_finite() + { + orientation.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), orientation).signum() + } else { + 0.0 + } * 1.3; + next.hold.scale = Vec3::one() * 0.0; + + if speed > 0.5 { + next.hand_l.position = Vec3::new(1.0 - s_a.hand.0, 4.0 + s_a.hand.1, 1.0 + s_a.hand.2); + next.hand_l.orientation = Quaternion::rotation_x(1.0); + + next.hand_r.position = Vec3::new(-1.0 + s_a.hand.0, -1.0 + s_a.hand.1, s_a.hand.2); + next.hand_r.orientation = Quaternion::rotation_x(0.4); + next.head.position = Vec3::new(0.0, 1.0 + s_a.head.0, -1.0 + s_a.head.1 + short * 0.06); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.06) + * Quaternion::rotation_x(head_look.y + 0.45); + + next.chest.position = Vec3::new(0.0, s_a.chest.0, -1.0 + s_a.chest.1 + shortalt * -0.5); + next.chest.orientation = Quaternion::rotation_z(0.3 + short * 0.08 + tilt * -0.2) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_x(-0.5); + + next.belt.position = Vec3::new(0.0, 0.5 + s_a.belt.0, 0.7 + s_a.belt.1); + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) + * Quaternion::rotation_y(tilt * 0.5) + * Quaternion::rotation_x(0.2); + + next.back.orientation = + Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); + + next.shorts.position = Vec3::new(0.0, 1.0 + s_a.shorts.0, 1.0 + s_a.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(short * 0.16 + tilt * -1.5) + * Quaternion::rotation_y(tilt * 0.7) + * Quaternion::rotation_x(0.3); + + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + foothoril * -10.5 * walkintensity - lower * 1.0, + 1.0 + s_a.foot.2 + ((footvertl * -1.7).max(-1.0)) * walkintensity, + ); + next.foot_l.orientation = + Quaternion::rotation_x(-0.2 + footrotl * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); + + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + foothorir * -10.5 * walkintensity - lower * 1.0, + 1.0 + s_a.foot.2 + ((footvertr * -1.7).max(-1.0)) * walkintensity, + ); + next.foot_r.orientation = + Quaternion::rotation_x(-0.2 + footrotr * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); + + next.shoulder_l.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); + + next.shoulder_r.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); + + next.lantern.orientation = + Quaternion::rotation_x(shorte * 0.2 + 0.4) * Quaternion::rotation_y(shorte * 0.1); + } else { + next.head.position = Vec3::new( + 0.0, + 1.0 + s_a.head.0, + -2.0 + s_a.head.1 + slow * 0.1 + breathe * -0.05, + ); + next.head.orientation = Quaternion::rotation_z(head_look.x) + * Quaternion::rotation_x(0.6 + head_look.y.abs()); + + next.chest.position = Vec3::new(0.0, s_a.chest.0, -3.0 + s_a.chest.1 + slow * 0.1); + next.chest.orientation = Quaternion::rotation_x(-0.7); + + next.belt.position = Vec3::new(0.0, s_a.belt.0, s_a.belt.1); + next.belt.orientation = Quaternion::rotation_z(0.3 + head_look.x * -0.1); + + next.hand_l.position = Vec3::new(1.0 - s_a.hand.0, 5.0 + s_a.hand.1, 0.0 + s_a.hand.2); + next.hand_l.orientation = Quaternion::rotation_x(1.35); + + next.hand_r.position = Vec3::new(-1.0 + s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_r.orientation = Quaternion::rotation_x(0.4); + + next.shorts.position = Vec3::new(0.0, s_a.shorts.0, s_a.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.6 + head_look.x * -0.2); + + next.foot_l.position = Vec3::new(-s_a.foot.0, -6.0 + s_a.foot.1, 1.0 + s_a.foot.2); + next.foot_l.orientation = Quaternion::rotation_x(-0.5); + + next.foot_r.position = Vec3::new(s_a.foot.0, 4.0 + s_a.foot.1, s_a.foot.2); + } + + if skeleton.holding_lantern { + next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1 + 5.0, s_a.hand.2 + 9.0); + next.hand_r.orientation = Quaternion::rotation_x(2.5); + + next.lantern.position = Vec3::new(0.0, 1.5, -5.5); + next.lantern.orientation = next.hand_r.orientation.inverse(); + } + let equip_slow = 1.0 + (anim_time * 12.0 + PI).cos(); + let equip_slowa = 1.0 + (anim_time * 12.0 + PI / 4.0).cos(); + next.hand_l.orientation = Quaternion::rotation_y(-2.3) * Quaternion::rotation_z(PI / 2.0); + next.hand_r.orientation = Quaternion::rotation_y(-2.3) * Quaternion::rotation_z(PI / 2.0); + next.control.position = Vec3::new(equip_slowa * -1.5, 0.0, equip_slow * 1.5); + + match active_tool_kind { + Some(ToolKind::Sword) => { + next.hand_l.position = Vec3::new(-8.0, -5.0, 17.0); + next.hand_r.position = Vec3::new(-6.0, -4.5, 14.0); + }, + Some(ToolKind::Axe) => { + next.hand_l.position = Vec3::new(-7.0, -5.0, 17.0); + next.hand_r.position = Vec3::new(-5.0, -4.5, 14.0); + }, + Some(ToolKind::Hammer | ToolKind::Pick) => { + next.hand_l.position = Vec3::new(-5.0, -5.0, 13.0); + next.hand_r.position = Vec3::new(-3.0, -4.5, 10.0); + }, + Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => { + next.hand_l.position = Vec3::new(-3.0, -5.0, 8.0); + next.hand_r.position = Vec3::new(-1.75, -4.5, 5.0); + }, + Some(ToolKind::Bow) => { + next.hand_l.position = Vec3::new(-3.0, -5.0, 9.0); + next.hand_r.position = Vec3::new(-1.75, -4.5, 7.0); + }, + _ => {}, + } + next + } +} diff --git a/voxygen/anim/src/character/sneakwield.rs b/voxygen/anim/src/character/sneakwield.rs new file mode 100644 index 0000000000..b20f743752 --- /dev/null +++ b/voxygen/anim/src/character/sneakwield.rs @@ -0,0 +1,376 @@ +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; +use common::comp::item::{Hands, ToolKind}; +use core::{f32::consts::PI, ops::Mul}; + +pub struct SneakWieldAnimation; + +impl Animation for SneakWieldAnimation { + #[allow(clippy::type_complexity)] + type Dependency<'a> = ( + Option, + Option, + (Option, Option), + Vec3, + Vec3, + Vec3, + f32, + ); + type Skeleton = CharacterSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"character_sneakwield\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "character_sneakwield")] + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + ( active_tool_kind, + second_tool_kind, + hands, velocity, orientation, last_ori, global_time): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + let speed = Vec2::::from(velocity).magnitude(); + *rate = 1.0; + let slow = (anim_time * 3.0).sin(); + let breathe = ((anim_time * 0.5).sin()).abs(); + let walkintensity = if speed > 5.0 { 1.0 } else { 0.45 }; + let lower = if speed > 5.0 { 0.0 } else { 1.0 }; + let _snapfoot = if speed > 5.0 { 1.1 } else { 2.0 }; + let lab: f32 = 1.0; + let foothoril = (anim_time * 7.0 * lab + PI * 1.45).sin(); + let foothorir = (anim_time * 7.0 * lab + PI * (0.45)).sin(); + let speednorm = speed / 4.0; + + let footvertl = (anim_time * 7.0 * lab).sin(); + let footvertr = (anim_time * 7.0 * lab + PI).sin(); + + let footrotl = ((5.0 / (2.5 + (2.5) * ((anim_time * 7.0 * lab + PI * 1.4).sin()).powi(2))) + .sqrt()) + * ((anim_time * 7.0 * lab + PI * 1.4).sin()); + + let footrotr = ((5.0 / (1.0 + (4.0) * ((anim_time * 7.0 * lab + PI * 0.4).sin()).powi(2))) + .sqrt()) + * ((anim_time * 7.0 * lab + PI * 0.4).sin()); + + let short = (anim_time * lab * 7.0).sin(); + let noisea = (anim_time * 11.0 + PI / 6.0).sin(); + let noiseb = (anim_time * 19.0 + PI / 4.0).sin(); + + let shorte = ((5.0 / (4.0 + 1.0 * ((anim_time * lab * 7.0).sin()).powi(2))).sqrt()) + * ((anim_time * lab * 7.0).sin()); + + let shortalt = (anim_time * lab * 7.0 + PI / 2.0).sin(); + + let head_look = Vec2::new( + (global_time + anim_time / 18.0).floor().mul(7331.0).sin() * 0.2, + (global_time + anim_time / 18.0).floor().mul(1337.0).sin() * 0.1, + ); + + let orientation: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(orientation, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && orientation.angle_between(last_ori).is_finite() + { + orientation.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), orientation).signum() + } else { + 0.0 + } * 1.3; + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_x(0.0); + next.hold.scale = Vec3::one() * 0.0; + + if speed > 0.5 { + next.hand_l.position = Vec3::new(1.0 - s_a.hand.0, 4.0 + s_a.hand.1, 1.0 + s_a.hand.2); + next.hand_l.orientation = Quaternion::rotation_x(1.0); + + next.hand_r.position = Vec3::new(-1.0 + s_a.hand.0, -1.0 + s_a.hand.1, s_a.hand.2); + next.hand_r.orientation = Quaternion::rotation_x(0.4); + next.head.position = Vec3::new(0.0, 1.0 + s_a.head.0, -1.0 + s_a.head.1 + short * 0.06); + next.head.orientation = + Quaternion::rotation_z(tilt * -2.5 + head_look.x * 0.2 - short * 0.06) + * Quaternion::rotation_x(head_look.y + 0.45); + + next.chest.position = Vec3::new(0.0, s_a.chest.0, -1.0 + s_a.chest.1 + shortalt * -0.5); + next.chest.orientation = Quaternion::rotation_z(0.3 + short * 0.08 + tilt * -0.2) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_x(-0.5); + + next.belt.position = Vec3::new(0.0, 0.5 + s_a.belt.0, 0.7 + s_a.belt.1); + next.belt.orientation = Quaternion::rotation_z(short * 0.1 + tilt * -1.1) + * Quaternion::rotation_y(tilt * 0.5) + * Quaternion::rotation_x(0.2); + + next.back.orientation = + Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1); + + next.shorts.position = Vec3::new(0.0, 1.0 + s_a.shorts.0, 1.0 + s_a.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(short * 0.16 + tilt * -1.5) + * Quaternion::rotation_y(tilt * 0.7) + * Quaternion::rotation_x(0.3); + + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + foothoril * -10.5 * walkintensity - lower * 1.0, + 1.0 + s_a.foot.2 + ((footvertl * -1.7).max(-1.0)) * walkintensity, + ); + next.foot_l.orientation = + Quaternion::rotation_x(-0.2 + footrotl * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); + + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + foothorir * -10.5 * walkintensity - lower * 1.0, + 1.0 + s_a.foot.2 + ((footvertr * -1.7).max(-1.0)) * walkintensity, + ); + next.foot_r.orientation = + Quaternion::rotation_x(-0.2 + footrotr * -0.8 * walkintensity) + * Quaternion::rotation_y(tilt * 1.8); + + next.shoulder_l.orientation = Quaternion::rotation_x(short * 0.15 * walkintensity); + + next.shoulder_r.orientation = Quaternion::rotation_x(short * -0.15 * walkintensity); + + next.lantern.orientation = + Quaternion::rotation_x(shorte * 0.2 + 0.4) * Quaternion::rotation_y(shorte * 0.1); + } else { + next.head.position = Vec3::new( + 0.0, + 1.0 + s_a.head.0, + -2.0 + s_a.head.1 + slow * 0.1 + breathe * -0.05, + ); + next.head.orientation = Quaternion::rotation_z(head_look.x) + * Quaternion::rotation_x(0.6 + head_look.y.abs()); + + next.chest.position = Vec3::new(0.0, s_a.chest.0, -3.0 + s_a.chest.1 + slow * 0.1); + next.chest.orientation = Quaternion::rotation_x(-0.7); + + next.belt.position = Vec3::new(0.0, s_a.belt.0, s_a.belt.1); + next.belt.orientation = Quaternion::rotation_z(0.3 + head_look.x * -0.1); + + next.hand_l.position = Vec3::new(1.0 - s_a.hand.0, 5.0 + s_a.hand.1, 0.0 + s_a.hand.2); + next.hand_l.orientation = Quaternion::rotation_x(1.35); + + next.hand_r.position = Vec3::new(-1.0 + s_a.hand.0, s_a.hand.1, s_a.hand.2); + next.hand_r.orientation = Quaternion::rotation_x(0.4); + + next.shorts.position = Vec3::new(0.0, s_a.shorts.0, s_a.shorts.1); + next.shorts.orientation = Quaternion::rotation_z(0.6 + head_look.x * -0.2); + + next.foot_l.position = Vec3::new(-s_a.foot.0, -6.0 + s_a.foot.1, 1.0 + s_a.foot.2); + next.foot_l.orientation = Quaternion::rotation_x(-0.5); + + next.foot_r.position = Vec3::new(s_a.foot.0, 4.0 + s_a.foot.1, s_a.foot.2); + } + + if skeleton.holding_lantern { + next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1 + 5.0, s_a.hand.2 + 9.0); + next.hand_r.orientation = Quaternion::rotation_x(2.5); + + next.lantern.position = Vec3::new(0.0, 1.5, -5.5); + next.lantern.orientation = next.hand_r.orientation.inverse(); + } + + match (hands, active_tool_kind, second_tool_kind) { + ((Some(Hands::Two), _), tool, _) | ((None, Some(Hands::Two)), _, tool) => match tool { + Some(ToolKind::Sword) => { + next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2); + next.hand_l.orientation = + Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4); + next.hand_r.position = Vec3::new(s_a.shr.0, s_a.shr.1, s_a.shr.2); + next.hand_r.orientation = + Quaternion::rotation_x(s_a.shr.3) * Quaternion::rotation_y(s_a.shr.4); + + next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1 - 3.0, s_a.sc.2); + next.control.orientation = + Quaternion::rotation_x(s_a.sc.3) * Quaternion::rotation_z(0.0); + }, + Some(ToolKind::Axe) => { + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); + + if speed < 0.5 { + next.head.position = Vec3::new(0.0, 0.0 + s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_z(head_look.x) + * Quaternion::rotation_x(0.35 + head_look.y.abs()); + next.chest.orientation = Quaternion::rotation_x(-0.35) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.15); + next.belt.position = Vec3::new(0.0, 1.0 + s_a.belt.0, s_a.belt.1); + next.belt.orientation = Quaternion::rotation_x(0.15) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.15); + next.shorts.position = Vec3::new(0.0, 1.0 + s_a.shorts.0, s_a.shorts.1); + next.shorts.orientation = + Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25); + } else { + } + next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2); + next.hand_l.orientation = + Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4); + next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2); + next.hand_r.orientation = + Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5); + + next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2); + next.control.orientation = Quaternion::rotation_x(s_a.ac.3) + * Quaternion::rotation_y(s_a.ac.4) + * Quaternion::rotation_z(s_a.ac.5); + }, + Some(ToolKind::Hammer) | Some(ToolKind::Pick) => { + next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2); + next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3) + * Quaternion::rotation_y(s_a.hhl.4) + * Quaternion::rotation_z(s_a.hhl.5); + next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2); + next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3) + * Quaternion::rotation_y(s_a.hhr.4) + * Quaternion::rotation_z(s_a.hhr.5); + + next.control.position = + Vec3::new(s_a.hc.0, s_a.hc.1 + speed * 0.2, s_a.hc.2 + 6.0); + next.control.orientation = Quaternion::rotation_x(s_a.hc.3) + * Quaternion::rotation_y(s_a.hc.4 + speed * -0.04) + * Quaternion::rotation_z(s_a.hc.5); + }, + Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => { + if speed > 0.5 && velocity.z == 0.0 { + next.hand_r.position = Vec3::new( + 7.0 + s_a.hand.0 + foothoril * 1.3, + -4.0 + s_a.hand.1 + foothoril * -7.0, + 1.0 + s_a.hand.2 - foothoril * 5.5, + ); + next.hand_r.orientation = Quaternion::rotation_x(0.6 + footrotl * -1.2) + * Quaternion::rotation_y(footrotl * -0.4); + } else { + next.hand_r.position = Vec3::new(s_a.sthr.0, s_a.sthr.1, s_a.sthr.2); + next.hand_r.orientation = + Quaternion::rotation_x(s_a.sthr.3) * Quaternion::rotation_y(s_a.sthr.4); + }; + + next.control.position = Vec3::new(s_a.stc.0, s_a.stc.1 - 2.0, s_a.stc.2 + 4.0); + + next.hand_l.position = Vec3::new(s_a.sthl.0, s_a.sthl.1, s_a.sthl.2); + next.hand_l.orientation = Quaternion::rotation_x(s_a.sthl.3); + + next.control.orientation = Quaternion::rotation_x(s_a.stc.3) + * Quaternion::rotation_y(s_a.stc.4) + * Quaternion::rotation_z(s_a.stc.5); + }, + Some(ToolKind::Bow) => { + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); + next.hand_l.position = Vec3::new(s_a.bhl.0, s_a.bhl.1, s_a.bhl.2); + next.hand_l.orientation = Quaternion::rotation_x(s_a.bhl.3); + next.hand_r.position = Vec3::new(s_a.bhr.0, s_a.bhr.1, s_a.bhr.2); + next.hand_r.orientation = Quaternion::rotation_x(s_a.bhr.3); + + next.hold.position = Vec3::new(0.0, -1.0, -5.2); + next.hold.orientation = Quaternion::rotation_x(-PI / 2.0); + next.hold.scale = Vec3::one() * 1.0; + + next.control.position = Vec3::new(s_a.bc.0 + 2.0, s_a.bc.1, s_a.bc.2 + 5.0); + next.control.orientation = Quaternion::rotation_x(speednorm * -0.5 + 0.8) + * Quaternion::rotation_y(s_a.bc.4) + * Quaternion::rotation_z(s_a.bc.5); + }, + Some(ToolKind::Debug) => { + next.hand_l.position = Vec3::new(-7.0, 4.0, 3.0); + next.hand_l.orientation = Quaternion::rotation_x(1.27); + next.main.position = Vec3::new(-5.0, 5.0, 23.0); + next.main.orientation = Quaternion::rotation_x(PI); + }, + Some(ToolKind::Farming) => { + if speed < 0.5 { + next.head.orientation = Quaternion::rotation_z(head_look.x) + * Quaternion::rotation_x(-0.2 + head_look.y.abs()); + } + next.hand_l.position = Vec3::new(9.0, 1.0, 1.0); + next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0); + next.hand_r.position = Vec3::new(9.0, 1.0, 11.0); + next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0); + next.main.position = Vec3::new(7.5, 7.5, 13.2); + next.main.orientation = Quaternion::rotation_y(PI); + + next.control.position = Vec3::new(-11.0 + slow * 2.0, 1.8, 4.0); + next.control.orientation = Quaternion::rotation_x(0.0) + * Quaternion::rotation_y(0.6) + * Quaternion::rotation_z(0.0); + }, + _ => {}, + }, + ((_, _), _, _) => {}, + }; + match hands { + (Some(Hands::One), _) => { + next.control_l.position = Vec3::new(-7.0, 6.0, 5.0); + next.control_l.orientation = Quaternion::rotation_x(-0.3); + next.hand_l.position = Vec3::new(-1.0, -0.5, 0.0); + next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0) + }, + (_, _) => {}, + }; + match hands { + (None | Some(Hands::One), Some(Hands::One)) => { + next.control_r.position = Vec3::new(7.0, 6.0, 5.0); + next.control_r.orientation = Quaternion::rotation_x(-0.3); + next.hand_r.position = Vec3::new(1.0, -0.5, 0.0); + next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0) + }, + (_, _) => {}, + }; + match hands { + (None, None) | (None, Some(Hands::One)) => { + next.hand_l.position = Vec3::new(-8.0, 2.0, 1.0); + next.hand_l.orientation = + Quaternion::rotation_x(0.5) * Quaternion::rotation_y(0.25); + }, + (_, _) => {}, + }; + match hands { + (None, None) | (Some(Hands::One), None) => { + next.hand_r.position = Vec3::new(8.0, 2.0, 1.0); + next.hand_r.orientation = + Quaternion::rotation_x(0.5) * Quaternion::rotation_y(-0.25); + }, + (_, _) => {}, + }; + + if let (None, Some(Hands::Two)) = hands { + next.second = next.main; + } + + if skeleton.holding_lantern { + next.hand_r.position = Vec3::new( + s_a.hand.0 - head_look.x * 6.0, + s_a.hand.1 + 5.0 - head_look.y * 10.0 + slow * 0.15, + s_a.hand.2 + 12.0 + head_look.y * 6.0 + slow * 0.5, + ); + next.hand_r.orientation = Quaternion::rotation_x(2.25 + slow * -0.06) + * Quaternion::rotation_z(0.9) + * Quaternion::rotation_y(head_look.x * 1.5) + * Quaternion::rotation_x(head_look.y * 1.5); + + let fast = (anim_time * 8.0).sin(); + let fast2 = (anim_time * 6.0 + 8.0).sin(); + + next.lantern.position = Vec3::new(-0.5, -0.5, -2.5); + next.lantern.orientation = next.hand_r.orientation.inverse() + * Quaternion::rotation_x((fast + 0.5) * 1.0 * speednorm + fast * 0.1) + * Quaternion::rotation_y( + tilt * 1.0 * fast + tilt * 1.0 + fast2 * speednorm * 0.25 + fast2 * 0.1, + ); + } + next + } +} diff --git a/voxygen/egui/src/character_states.rs b/voxygen/egui/src/character_states.rs index d27a8975cc..b2dec83c46 100644 --- a/voxygen/egui/src/character_states.rs +++ b/voxygen/egui/src/character_states.rs @@ -22,12 +22,11 @@ pub fn draw_char_state_group( CharacterState::ChargedMelee(data) => charged_melee_grid(ui, data), // Character states with no associated data to display CharacterState::Dance - | CharacterState::Idle + | CharacterState::Idle(_) | CharacterState::Sit | CharacterState::GlideWield(_) - | CharacterState::Sneak | CharacterState::Talk - | CharacterState::Wielding => {}, + | CharacterState::Wielding(_) => {}, CharacterState::LeapMelee(data) => leap_melee_grid(ui, data), _ => { ui.label(""); diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index 05515813d4..14fb308459 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -25,6 +25,7 @@ fn maps_wield_while_equipping() { buildup_duration: Duration::from_millis(10), }, timer: Duration::default(), + is_sneaking: false, }), &PreviousEntityState { event: SfxEvent::Idle, diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 8a55d371fe..132f34e17e 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -201,7 +201,7 @@ impl MovementEventMapper { { return if matches!(character_state, CharacterState::Roll(_)) { SfxEvent::Roll - } else if matches!(character_state, CharacterState::Sneak) { + } else if character_state.is_stealthy() { SfxEvent::Sneak } else { match underfoot_block_kind { diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index 047cc80a9f..8a31168fc9 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -93,7 +93,7 @@ fn same_previous_event_elapsed_emits() { #[test] fn maps_idle() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &PhysicsState { on_ground: Some(Block::empty()), ..Default::default() @@ -115,7 +115,7 @@ fn maps_idle() { #[test] fn maps_run_with_sufficient_velocity() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &PhysicsState { on_ground: Some(Block::empty()), ..Default::default() @@ -137,7 +137,7 @@ fn maps_run_with_sufficient_velocity() { #[test] fn does_not_map_run_with_insufficient_velocity() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &PhysicsState { on_ground: Some(Block::empty()), ..Default::default() @@ -159,7 +159,7 @@ fn does_not_map_run_with_insufficient_velocity() { #[test] fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &Default::default(), &PreviousEntityState { event: SfxEvent::Idle, @@ -190,7 +190,7 @@ fn maps_roll() { timer: Duration::default(), stage_section: states::utils::StageSection::Buildup, was_wielded: true, - was_sneak: false, + is_sneaking: false, was_combo: None, }), &PhysicsState { @@ -214,7 +214,7 @@ fn maps_roll() { #[test] fn maps_land_on_ground_to_run() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &PhysicsState { on_ground: Some(Block::empty()), ..Default::default() @@ -274,7 +274,7 @@ fn maps_glide() { #[test] fn maps_glider_close_when_closing_mid_flight() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &Default::default(), &PreviousEntityState { event: SfxEvent::Glide, @@ -294,7 +294,7 @@ fn maps_glider_close_when_closing_mid_flight() { #[ignore] fn maps_glider_close_when_landing() { let result = MovementEventMapper::map_movement_event( - &CharacterState::Idle {}, + &CharacterState::Idle(common::states::idle::Data { is_sneaking: false }), &PhysicsState { on_ground: Some(Block::empty()), ..Default::default() diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index e0d87e8fc0..b50098b474 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -34,7 +34,7 @@ use common::{ LightEmitter, Mounting, Ori, PhysicsState, PoiseState, Pos, Scale, Vel, }, resources::DeltaTime, - states::utils::StageSection, + states::{equipping, idle, utils::StageSection, wielding}, terrain::TerrainChunk, uid::UidAllocator, vol::RectRasterableVol, @@ -1117,7 +1117,7 @@ impl FigureMgr { skeleton_attr, ) }, - CharacterState::Sneak { .. } => { + CharacterState::Idle(idle::Data { is_sneaking: true }) => { anim::character::SneakAnimation::update_skeleton( &target_base, ( @@ -1502,19 +1502,36 @@ impl FigureMgr { skeleton_attr, ) }, - CharacterState::Equipping { .. } => { - anim::character::EquipAnimation::update_skeleton( - &target_base, - ( - active_tool_kind, - second_tool_kind, - rel_vel.magnitude(), - time, - ), - state.state_time, - &mut state_animation_rate, - skeleton_attr, - ) + CharacterState::Equipping(equipping::Data { is_sneaking, .. }) => { + if *is_sneaking { + anim::character::SneakEquipAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + rel_vel, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + time, + ), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + } else { + anim::character::EquipAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + second_tool_kind, + rel_vel.magnitude(), + time, + ), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + } }, CharacterState::Talk => anim::character::TalkAnimation::update_skeleton( &target_base, @@ -1529,7 +1546,7 @@ impl FigureMgr { &mut state_animation_rate, skeleton_attr, ), - CharacterState::Wielding { .. } => { + CharacterState::Wielding(wielding::Data { is_sneaking, .. }) => { if physics.in_liquid().is_some() { anim::character::SwimWieldAnimation::update_skeleton( &target_base, @@ -1544,6 +1561,23 @@ impl FigureMgr { &mut state_animation_rate, skeleton_attr, ) + } else if *is_sneaking { + anim::character::SneakWieldAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + second_tool_kind, + hands, + rel_vel, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + time, + ), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) } else { anim::character::WieldAnimation::update_skeleton( &target_base, @@ -4594,9 +4628,16 @@ impl FigureMgr { let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), - _ => (&CharacterState::Idle, &Last { - 0: CharacterState::Idle, - }), + _ => ( + &CharacterState::Idle(common::states::idle::Data { + is_sneaking: false, + }), + &Last { + 0: CharacterState::Idle(common::states::idle::Data { + is_sneaking: false, + }), + }, + ), }; if !character.same_variant(&last_character.0) { @@ -4712,9 +4753,16 @@ impl FigureMgr { let (character, last_character) = match (character, last_character) { (Some(c), Some(l)) => (c, l), - _ => (&CharacterState::Idle, &Last { - 0: CharacterState::Idle, - }), + _ => ( + &CharacterState::Idle(common::states::idle::Data { + is_sneaking: false, + }), + &Last { + 0: CharacterState::Idle(common::states::idle::Data { + is_sneaking: false, + }), + }, + ), }; if !character.same_variant(&last_character.0) {