diff --git a/client/src/lib.rs b/client/src/lib.rs index 56147e5fb7..0b97b35bc8 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -393,11 +393,11 @@ impl Client { { if last_character_states .get(entity) - .map(|&l| !client_character_state.equals(&l.0)) + .map(|l| !client_character_state.equals(&l.0)) .unwrap_or(true) { let _ = last_character_states - .insert(entity, comp::Last(*client_character_state)); + .insert(entity, comp::Last(client_character_state.clone())); } } } diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 7dbe2fa18a..04cb2a3ea2 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1,17 +1,27 @@ use crate::{ - comp::{CharacterState, Item, ToolData}, + comp::{Body, CharacterState, Item, Projectile, ToolData}, states::*, }; use specs::{Component, DenseVecStorage, FlaggedStorage, HashMapStorage}; use std::time::Duration; +use vek::vec::Vec3; -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] pub enum CharacterAbility { - BasicAttack { + BasicMelee { buildup_duration: Duration, recover_duration: Duration, base_damage: u32, }, + BasicRanged { + recover_duration: Duration, + projectile: Projectile, + projectile_body: Body, + }, + Boost { + duration: Duration, + only_up: bool, + }, BasicBlock, Roll, ChargeAttack, @@ -26,7 +36,7 @@ impl Component for CharacterAbility { type Storage = DenseVecStorage; } -#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct ItemConfig { pub item: Item, pub primary_ability: Option, @@ -35,7 +45,7 @@ pub struct ItemConfig { pub dodge_ability: Option, } -#[derive(Clone, PartialEq, Eq, Hash, Default, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] pub struct Loadout { pub active_item: Option, pub second_item: Option, @@ -48,39 +58,52 @@ pub struct Loadout { pub foot: Option, } -impl From for CharacterState { - fn from(ability: CharacterAbility) -> Self { +impl From<&CharacterAbility> for CharacterState { + fn from(ability: &CharacterAbility) -> Self { match ability { - CharacterAbility::BasicAttack { + CharacterAbility::BasicMelee { buildup_duration, recover_duration, base_damage, - } => CharacterState::BasicAttack(basic_attack::Data { + } => CharacterState::BasicMelee(basic_melee::Data { exhausted: false, - buildup_duration, - recover_duration, - base_damage, + buildup_duration: *buildup_duration, + recover_duration: *recover_duration, + base_damage: *base_damage, }), - CharacterAbility::BasicBlock { .. } => CharacterState::BasicBlock, - CharacterAbility::Roll { .. } => CharacterState::Roll(roll::Data { + CharacterAbility::BasicRanged { + recover_duration, + projectile, + projectile_body, + } => CharacterState::BasicRanged(basic_ranged::Data { + exhausted: false, + prepare_timer: Duration::default(), + recover_duration: *recover_duration, + projectile: projectile.clone(), + projectile_body: *projectile_body, + }), + CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data { + duration: *duration, + only_up: *only_up, + }), + CharacterAbility::BasicBlock => CharacterState::BasicBlock, + CharacterAbility::Roll => CharacterState::Roll(roll::Data { + remaining_duration: Duration::from_millis(600), + }), + CharacterAbility::ChargeAttack => CharacterState::ChargeAttack(charge_attack::Data { remaining_duration: Duration::from_millis(600), }), - CharacterAbility::ChargeAttack { .. } => { - CharacterState::ChargeAttack(charge_attack::Data { - remaining_duration: Duration::from_millis(600), - }) - }, CharacterAbility::TimedCombo { buildup_duration, recover_duration, base_damage, } => CharacterState::TimedCombo(timed_combo::Data { - buildup_duration, - recover_duration, + buildup_duration: *buildup_duration, + recover_duration: *recover_duration, stage: 0, stage_exhausted: false, stage_time_active: Duration::default(), - base_damage, + base_damage: *base_damage, }), } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 6c4ae39be9..e741e0393a 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -18,7 +18,7 @@ pub struct StateUpdate { pub server_events: VecDeque, } -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum CharacterState { Idle, Climb, @@ -34,8 +34,12 @@ pub enum CharacterState { ChargeAttack(charge_attack::Data), /// A dodge where player can roll Roll(roll::Data), - /// A basic attacking state - BasicAttack(basic_attack::Data), + /// A basic melee attack (e.g. sword) + BasicMelee(basic_melee::Data), + /// A basic ranged attack (e.g. bow) + BasicRanged(basic_ranged::Data), + /// A force will boost you into a direction for some duration + Boost(boost::Data), /// A three-stage attack where play must click at appropriate times /// to continue attack chain. TimedCombo(timed_combo::Data), @@ -48,7 +52,7 @@ impl CharacterState { pub fn is_wield(&self) -> bool { match self { CharacterState::Wielding - | CharacterState::BasicAttack(_) + | CharacterState::BasicMelee(_) | CharacterState::TimedCombo(_) | CharacterState::BasicBlock => true, _ => false, @@ -57,7 +61,8 @@ impl CharacterState { pub fn is_attack(&self) -> bool { match self { - CharacterState::BasicAttack(_) + CharacterState::BasicMelee(_) + | CharacterState::BasicRanged(_) | CharacterState::TimedCombo(_) | CharacterState::ChargeAttack(_) => true, _ => false, diff --git a/common/src/comp/inventory/item.rs b/common/src/comp/inventory/item.rs index 07e3e05dda..7777bde801 100644 --- a/common/src/comp/inventory/item.rs +++ b/common/src/comp/inventory/item.rs @@ -1,6 +1,9 @@ use crate::{ assets::{self, Asset}, - comp::{body::humanoid, CharacterAbility}, + comp::{ + body::{humanoid, object}, + projectile, Body, CharacterAbility, HealthChange, HealthSource, Projectile, + }, effect::Effect, terrain::{Block, BlockKind}, }; @@ -37,35 +40,61 @@ impl ToolData { use ToolKind::*; match self.kind { - Sword(_) => vec![BasicAttack { + Sword(_) => vec![BasicMelee { buildup_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(500), base_damage: 60, }], - Axe => vec![BasicAttack { + Axe => vec![BasicMelee { buildup_duration: Duration::from_millis(700), recover_duration: Duration::from_millis(100), base_damage: 80, }], - Hammer => vec![BasicAttack { + Hammer => vec![BasicMelee { buildup_duration: Duration::from_millis(700), recover_duration: Duration::from_millis(300), base_damage: 100, }], - Bow => vec![], - Dagger => vec![BasicAttack { + Bow => vec![BasicRanged { + projectile: Projectile { + hit_ground: vec![projectile::Effect::Stick], + hit_wall: vec![projectile::Effect::Stick], + hit_entity: vec![ + projectile::Effect::Damage(HealthChange { + // TODO: This should not be fixed (?) + amount: -30, + cause: HealthSource::Item, + }), + projectile::Effect::Vanish, + ], + time_left: Duration::from_secs(15), + owner: None, + }, + projectile_body: Body::Object(object::Body::Arrow), + recover_duration: Duration::from_millis(300), + }], + Dagger => vec![BasicMelee { buildup_duration: Duration::from_millis(100), recover_duration: Duration::from_millis(400), base_damage: 50, }], - Staff => vec![BasicAttack { + Staff => vec![BasicMelee { buildup_duration: Duration::from_millis(400), recover_duration: Duration::from_millis(300), base_damage: 70, }], Shield => vec![], Debug(kind) => match kind { - Boost => vec![], + DebugKind::Boost => vec![ + CharacterAbility::Boost { + duration: Duration::from_millis(100), + only_up: false, + }, + CharacterAbility::Boost { + duration: Duration::from_millis(100), + only_up: true, + }, + ], Possess => vec![], }, } diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 785c1e318d..27e997b08e 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -3,7 +3,7 @@ use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; use std::time::Duration; -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Effect { Damage(comp::HealthChange), Vanish, @@ -11,15 +11,15 @@ pub enum Effect { Possess, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Projectile { - pub owner: Uid, // TODO: use SmallVec for these effects pub hit_ground: Vec, pub hit_wall: Vec, pub hit_entity: Vec, /// Time left until the projectile will despawn pub time_left: Duration, + pub owner: Option, } impl Component for Projectile { diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index efb78ad323..e238a488b5 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -18,7 +18,7 @@ impl CharacterBehavior for Data { vel: *data.vel, ori: *data.ori, energy: *data.energy, - character: *data.character, + character: data.character.clone(), local_events: VecDeque::new(), server_events: VecDeque::new(), }; diff --git a/common/src/states/basic_attack.rs b/common/src/states/basic_melee.rs similarity index 75% rename from common/src/states/basic_attack.rs rename to common/src/states/basic_melee.rs index b81fba8de1..25dbe9bd6b 100644 --- a/common/src/states/basic_attack.rs +++ b/common/src/states/basic_melee.rs @@ -1,6 +1,6 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate}, - states::{utils::*, wielding}, + states::utils::*, sys::character_behavior::*, }; use std::{collections::VecDeque, time::Duration}; @@ -24,16 +24,16 @@ impl CharacterBehavior for Data { vel: *data.vel, ori: *data.ori, energy: *data.energy, - character: *data.character, + character: data.character.clone(), local_events: VecDeque::new(), server_events: VecDeque::new(), }; handle_move(data, &mut update); - // Build up if self.buildup_duration != Duration::default() { - update.character = CharacterState::BasicAttack(Data { + // Build up + update.character = CharacterState::BasicMelee(Data { buildup_duration: self .buildup_duration .checked_sub(Duration::from_secs_f32(data.dt.0)) @@ -42,9 +42,8 @@ impl CharacterBehavior for Data { base_damage: self.base_damage, exhausted: false, }); - } - // Hit attempt - else if !self.exhausted { + } else if !self.exhausted { + // Hit attempt if let Some(tool) = unwrap_tool_data(data) { data.updater.insert(data.entity, Attacking { base_damage: self.base_damage, @@ -53,16 +52,15 @@ impl CharacterBehavior for Data { }); } - update.character = CharacterState::BasicAttack(Data { + update.character = CharacterState::BasicMelee(Data { buildup_duration: self.buildup_duration, recover_duration: self.recover_duration, base_damage: self.base_damage, exhausted: true, }); - } - // Recovery - else if self.recover_duration != Duration::default() { - update.character = CharacterState::BasicAttack(Data { + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::BasicMelee(Data { buildup_duration: self.buildup_duration, recover_duration: self .recover_duration @@ -71,16 +69,11 @@ impl CharacterBehavior for Data { base_damage: self.base_damage, exhausted: true, }); - } - // Done - else { - if let Some(tool) = unwrap_tool_data(data) { - update.character = CharacterState::Wielding; - // Make sure attack component is removed - data.updater.remove::(data.entity); - } else { - update.character = CharacterState::Idle; - } + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); } // Grant energy on successful hit diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs new file mode 100644 index 0000000000..533759d031 --- /dev/null +++ b/common/src/states/basic_ranged.rs @@ -0,0 +1,87 @@ +use crate::{ + comp::{Attacking, Body, CharacterState, EnergySource, Gravity, Projectile, StateUpdate}, + event::ServerEvent, + states::{utils::*, wielding}, + sys::character_behavior::*, +}; +use std::{collections::VecDeque, time::Duration}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// How long we prepared the weapon already + pub prepare_timer: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Projectile + pub projectile: Projectile, + /// Projectile + pub projectile_body: Body, + /// Whether the attack fired already + pub exhausted: bool, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + character: data.character.clone(), + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; + + handle_move(data, &mut update); + handle_jump(data, &mut update); + + if !self.exhausted + && (data.inputs.primary.is_pressed() | data.inputs.secondary.is_pressed()) + { + // Prepare (draw the bow) + update.character = CharacterState::BasicRanged(Data { + prepare_timer: self.prepare_timer + Duration::from_secs_f32(data.dt.0), + recover_duration: self.recover_duration, + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + exhausted: false, + }); + } else if !self.exhausted { + // Fire + update.server_events.push_front(ServerEvent::Shoot { + entity: data.entity, + dir: data.inputs.look_dir, + body: self.projectile_body, + light: None, + projectile: self.projectile.clone(), + gravity: Some(Gravity(0.1)), + }); + + update.character = CharacterState::BasicRanged(Data { + prepare_timer: self.prepare_timer, + recover_duration: self.recover_duration, + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + exhausted: true, + }); + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::BasicRanged(Data { + prepare_timer: self.prepare_timer, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + exhausted: true, + }); + return update; + } else { + // Done + update.character = CharacterState::Wielding; + } + + update + } +} diff --git a/common/src/states/boost.rs b/common/src/states/boost.rs new file mode 100644 index 0000000000..7acfd6bc80 --- /dev/null +++ b/common/src/states/boost.rs @@ -0,0 +1,59 @@ +use crate::{ + comp::{Attacking, CharacterState, EnergySource, StateUpdate}, + states::{utils::*, wielding}, + sys::character_behavior::*, +}; +use std::{collections::VecDeque, time::Duration}; + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// How long the state has until exiting + pub duration: Duration, + pub only_up: bool, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + character: data.character.clone(), + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; + + handle_move(data, &mut update); + + // Still going + if self.duration != Duration::default() { + if self.only_up { + update.vel.0.z = 30.0; + } else { + update.vel.0 = data.inputs.look_dir * 30.0; + } + update.character = CharacterState::Boost(Data { + duration: self + .duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + only_up: self.only_up, + }); + } + // Done + else { + update.character = CharacterState::Wielding; + } + + // Grant energy on successful hit + if let Some(attack) = data.attacking { + if attack.applied && attack.hit_count > 0 { + data.updater.remove::(data.entity); + update.energy.change_by(100, EnergySource::HitEnemy); + } + } + + update + } +} diff --git a/common/src/states/charge_attack.rs b/common/src/states/charge_attack.rs index af479613b1..6c41e1ffeb 100644 --- a/common/src/states/charge_attack.rs +++ b/common/src/states/charge_attack.rs @@ -21,7 +21,7 @@ impl CharacterBehavior for Data { pos: *data.pos, vel: *data.vel, ori: *data.ori, - character: *data.character, + character: data.character.clone(), energy: *data.energy, local_events: VecDeque::new(), server_events: VecDeque::new(), diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 0bff46fdad..27f51c8da5 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -24,7 +24,7 @@ impl CharacterBehavior for Data { pos: *data.pos, vel: *data.vel, ori: *data.ori, - character: *data.character, + character: data.character.clone(), energy: *data.energy, local_events: VecDeque::new(), server_events: VecDeque::new(), diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index 9fac0bbd83..66506e39c8 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -14,7 +14,7 @@ pub struct Data { impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { - character: *data.character, + character: data.character.clone(), pos: *data.pos, vel: *data.vel, ori: *data.ori, diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 78851f006a..4d7a73eddf 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -20,7 +20,7 @@ impl CharacterBehavior for Data { vel: *data.vel, ori: *data.ori, energy: *data.energy, - character: *data.character, + character: data.character.clone(), local_events: VecDeque::new(), server_events: VecDeque::new(), }; diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index 7d78d9b73f..76be0b8f67 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -10,7 +10,7 @@ pub struct Data; impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { - character: *data.character, + character: data.character.clone(), pos: *data.pos, vel: *data.vel, ori: *data.ori, diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 3598c5f7fb..f66ee24ffe 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -1,6 +1,7 @@ -// Module declarations -pub mod basic_attack; pub mod basic_block; +pub mod basic_melee; +pub mod basic_ranged; +pub mod boost; pub mod charge_attack; pub mod climb; pub mod equipping; diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 8d57e56546..2d0795d95c 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -15,7 +15,7 @@ pub struct Data { impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { - character: *data.character, + character: data.character.clone(), pos: *data.pos, vel: *data.vel, ori: *data.ori, diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index 407e176c01..487206094a 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -11,7 +11,7 @@ pub struct Data; impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { - character: *data.character, + character: data.character.clone(), pos: *data.pos, vel: *data.vel, ori: *data.ori, diff --git a/common/src/states/timed_combo.rs b/common/src/states/timed_combo.rs index 7687852645..cd7cd7b9d9 100644 --- a/common/src/states/timed_combo.rs +++ b/common/src/states/timed_combo.rs @@ -26,7 +26,7 @@ impl CharacterBehavior for Data { vel: *data.vel, ori: *data.ori, energy: *data.energy, - character: *data.character, + character: data.character.clone(), local_events: VecDeque::new(), server_events: VecDeque::new(), }; diff --git a/common/src/states/triple_strike.rs b/common/src/states/triple_strike.rs index fd10f3ccb6..2fe224f265 100644 --- a/common/src/states/triple_strike.rs +++ b/common/src/states/triple_strike.rs @@ -33,7 +33,7 @@ impl CharacterBehavior for Data { vel: *data.vel, ori: *data.ori, energy: *data.energy, - character: *data.character, + character: data.character.clone(), local_events: VecDeque::new(), server_events: VecDeque::new(), }; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 9312256ea4..85136661a6 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -195,7 +195,7 @@ pub fn attempt_primary_ability(data: &JoinData, update: &mut StateUpdate) { .loadout .active_item .as_ref() - .and_then(|i| i.primary_ability) + .and_then(|i| i.primary_ability.as_ref()) { update.character = ability.into(); } @@ -217,7 +217,7 @@ pub fn attempt_secondary_ability(data: &JoinData, update: &mut StateUpdate) { .loadout .active_item .as_ref() - .and_then(|i| i.secondary_ability) + .and_then(|i| i.secondary_ability.as_ref()) { update.character = ability.into(); } @@ -246,7 +246,7 @@ pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) { .loadout .active_item .as_ref() - .and_then(|i| i.dodge_ability) + .and_then(|i| i.dodge_ability.as_ref()) { update.character = ability.into(); } diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index 6d2aaa491a..07f5ba358c 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -10,7 +10,7 @@ pub struct Data; impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { - character: *data.character, + character: data.character.clone(), pos: *data.pos, vel: *data.vel, ori: *data.ori, diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 1217a98147..ed2247749b 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -182,7 +182,9 @@ impl<'a> System<'a> for Sys { CharacterState::Equipping(data) => data.behavior(&j), CharacterState::ChargeAttack(data) => data.behavior(&j), CharacterState::TripleStrike(data) => data.behavior(&j), - CharacterState::BasicAttack(data) => data.behavior(&j), + CharacterState::BasicMelee(data) => data.behavior(&j), + CharacterState::BasicRanged(data) => data.behavior(&j), + CharacterState::Boost(data) => data.behavior(&j), CharacterState::TimedCombo(data) => data.behavior(&j), // Do not use default match. diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 99c1cc42bf..168035a4ca 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -77,8 +77,11 @@ impl<'a> System<'a> for Sys { entity, cause: HealthSource::World, }), - projectile::Effect::Possess => server_emitter - .emit(ServerEvent::Possess(projectile.owner.into(), other)), + projectile::Effect::Possess => { + if let Some(owner) = projectile.owner { + server_emitter.emit(ServerEvent::Possess(owner.into(), other)); + } + }, _ => {}, } } diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index d32eb3e1d2..f9a5dcfe19 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -283,17 +283,17 @@ impl<'a> System<'a> for Sys { } } - if let Some(&character_state) = character_state { + if let Some(&character_state) = character_state.as_ref() { if last_character_state .get(entity) - .map(|&l| !character_state.equals(&l.0)) + .map(|l| !character_state.equals(&l.0)) .unwrap_or(true) { - let _ = last_character_state.insert(entity, Last(character_state)); + let _ = last_character_state.insert(entity, Last(character_state.clone())); send_msg( ServerMsg::EntityCharacterState { entity: uid.into(), - character_state, + character_state: character_state.clone(), }, entity, pos, @@ -367,7 +367,7 @@ pub fn send_initial_unsynced_components( if let Some(&ori) = ori { client.notify(ServerMsg::EntityOri { entity, ori }); } - if let Some(&character_state) = character_state { + if let Some(character_state) = character_state.cloned() { client.notify(ServerMsg::EntityCharacterState { entity, character_state, diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 382e1aa27c..d9db6bbded 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -112,7 +112,7 @@ impl<'a> TrackedComps<'a> { .map(|c| comps.push(c.into())); self.character_state .get(entity) - .copied() + .cloned() .map(|c| comps.push(c.into())); EntityPackage { uid, comps } diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 98414df9a6..877132222d 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -543,7 +543,7 @@ impl<'a> Widget for Skillbar<'a> { // M1 Slot match self.character_state { - CharacterState::BasicAttack { .. } => { + CharacterState::BasicMelee { .. } => { if self.controller.primary.is_pressed() { let fade_pulse = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.6; //Animation timer; Image::new(self.imgs.skillbar_slot_big) @@ -654,7 +654,7 @@ impl<'a> Widget for Skillbar<'a> { .set(state.ids.m2_slot, ui); } },*/ - CharacterState::BasicAttack { .. } => { + CharacterState::BasicMelee { .. } => { let fade_pulse = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.6; //Animation timer; if self.controller.secondary.is_pressed() { Image::new(self.imgs.skillbar_slot_big) diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 5cf08e6cac..b8a00539a1 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -6,7 +6,7 @@ use crate::{ }; use common::{ assets::watch::ReloadIndicator, - comp::{Body, CharacterState, ItemKind, Loadout}, + comp::{Body, CharacterState, ItemKind, Loadout, ToolKind}, }; use hashbrown::{hash_map::Entry, HashMap}; use std::{ @@ -17,23 +17,26 @@ use std::{ #[derive(PartialEq, Eq, Hash, Clone)] enum FigureKey { Simple(Body), - Complex( - Body, - Option, - CameraMode, - Option, - ), + Complex(Body, CameraMode, Option), } #[derive(PartialEq, Eq, Hash, Clone)] -struct CharacterStateCacheKey { +struct CharacterCacheKey { state: Discriminant, // TODO: Can this be simplified? + active_tool: Option>, // TODO: Can this be simplified? } -impl From<&CharacterState> for CharacterStateCacheKey { - fn from(cs: &CharacterState) -> Self { +impl CharacterCacheKey { + fn from(cs: &CharacterState, loadout: &Loadout) -> Self { Self { state: discriminant(&cs), + active_tool: if let Some(ItemKind::Tool(tool)) = + loadout.active_item.as_ref().map(|i| &i.item.kind) + { + Some(discriminant(&tool.kind)) + } else { + None + }, } } } @@ -67,12 +70,11 @@ impl FigureModelCache { for<'a> &'a common::comp::Body: std::convert::TryInto, Skel::Attr: Default, { - let key = if loadout.is_some() { + let key = if let Some(loadout) = loadout { FigureKey::Complex( body, - loadout.cloned(), camera_mode, - character_state.map(|cs| CharacterStateCacheKey::from(cs)), + character_state.map(|cs| CharacterCacheKey::from(cs, loadout)), ) } else { FigureKey::Simple(body) diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 85a2ad6de6..50015d73ba 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -468,7 +468,25 @@ impl FigureMgr { skeleton_attr, ) }, - CharacterState::BasicAttack(_) => { + CharacterState::BasicMelee(_) => { + anim::character::AttackAnimation::update_skeleton( + &target_base, + (active_tool_kind, time), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + }, + CharacterState::BasicRanged(_) => { + anim::character::AttackAnimation::update_skeleton( + &target_base, + (active_tool_kind, time), + state.state_time, + &mut state_animation_rate, + skeleton_attr, + ) + }, + CharacterState::Boost(_) => { anim::character::AttackAnimation::update_skeleton( &target_base, (active_tool_kind, time), diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index c0c97ecf38..a5517e134c 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -188,6 +188,13 @@ impl Scene { .get(scene_data.player_entity) .map_or(false, |cs| cs.is_dodge()); + let player_running = scene_data + .state + .ecs() + .read_storage::() + .get(scene_data.player_entity) + .map_or(false, |v| v.0.magnitude_squared() > 0.5); + let player_scale = match scene_data .state .ecs() @@ -211,9 +218,11 @@ impl Scene { let up = match self.camera.get_mode() { CameraMode::FirstPerson => { if player_rolling { - player_scale * 0.8_f32 + player_scale * 0.8 + } else if player_running { + player_scale * 1.6 + (scene_data.state.get_time() as f32 * 17.0).sin() * 0.05 } else { - player_scale * 1.6_f32 + player_scale * 1.6 } }, CameraMode::ThirdPerson => 1.2,