From 538cb56b877dbf063844b25785d9365cbc4c0229 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 20 Aug 2021 00:23:39 -0400 Subject: [PATCH 01/15] Sprites now go through a character state to be picked up. --- client/src/lib.rs | 4 +- common/src/comp/character_state.rs | 5 + common/src/comp/controller.rs | 4 +- common/src/states/mod.rs | 1 + common/src/states/sprite_interact.rs | 184 +++++++++++++++++++++++++++ common/src/states/utils.rs | 114 ++++++++++++----- common/systems/src/stats.rs | 3 +- 7 files changed, 275 insertions(+), 40 deletions(-) create mode 100644 common/src/states/sprite_interact.rs diff --git a/client/src/lib.rs b/client/src/lib.rs index 018ca47a64..17eb989464 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1369,8 +1369,8 @@ impl Client { } pub fn collect_block(&mut self, pos: Vec3) { - self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent( - InventoryEvent::Collect(pos), + self.control_action(ControlAction::InventoryAction(InventoryAction::Collect( + pos, ))); } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index e3697f8678..9038594052 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -108,6 +108,9 @@ pub enum CharacterState { SpriteSummon(sprite_summon::Data), /// Handles logic for using an item so it is not simply instant UseItem(use_item::Data), + /// Handles logic for interacting with a sprite, e.g. using a chest or + /// picking a plant + SpriteInteract(sprite_interact::Data), } impl CharacterState { @@ -255,6 +258,7 @@ impl CharacterState { CharacterState::SelfBuff(data) => data.behavior(j), CharacterState::SpriteSummon(data) => data.behavior(j), CharacterState::UseItem(data) => data.behavior(j), + CharacterState::SpriteInteract(data) => data.behavior(j), } } @@ -295,6 +299,7 @@ impl CharacterState { CharacterState::SelfBuff(data) => data.handle_event(j, action), CharacterState::SpriteSummon(data) => data.handle_event(j, action), CharacterState::UseItem(data) => data.handle_event(j, action), + CharacterState::SpriteInteract(data) => data.handle_event(j, action), } } } diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 54604d49f1..90e85909f8 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -17,7 +17,6 @@ use vek::*; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum InventoryEvent { Pickup(Uid), - Collect(Vec3), Swap(InvSlotId, InvSlotId), SplitSwap(InvSlotId, InvSlotId), Drop(InvSlotId), @@ -35,6 +34,7 @@ pub enum InventoryAction { Drop(EquipSlot), Use(Slot), Sort, + Collect(Vec3), } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -61,6 +61,7 @@ impl From for InventoryManip { InventoryAction::Swap(equip, slot) => Self::Swap(Slot::Equip(equip), slot), InventoryAction::Drop(equip) => Self::Drop(Slot::Equip(equip)), InventoryAction::Sort => Self::Sort, + InventoryAction::Collect(collect) => Self::Collect(collect), } } } @@ -69,7 +70,6 @@ impl From for InventoryManip { fn from(inv_event: InventoryEvent) -> Self { match inv_event { InventoryEvent::Pickup(pickup) => Self::Pickup(pickup), - InventoryEvent::Collect(collect) => Self::Collect(collect), InventoryEvent::Swap(inv1, inv2) => { Self::Swap(Slot::Inventory(inv1), Slot::Inventory(inv2)) }, diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 7fcd03f9c2..608f815e6e 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -25,6 +25,7 @@ pub mod shockwave; pub mod sit; pub mod sneak; pub mod spin_melee; +pub mod sprite_interact; pub mod sprite_summon; pub mod stunned; pub mod talk; diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs new file mode 100644 index 0000000000..2d7570a881 --- /dev/null +++ b/common/src/states/sprite_interact.rs @@ -0,0 +1,184 @@ +use super::utils::*; +use crate::{ + comp::{CharacterState, InventoryManip, StateUpdate}, + event::ServerEvent, + states::behavior::{CharacterBehavior, JoinData}, + terrain::SpriteKind, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; +use vek::Vec3; + +/// Separated out to condense update portions of character state +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// Buildup to item use + pub buildup_duration: Duration, + /// Duration of item use + pub use_duration: Duration, + /// Recovery after item use + pub recover_duration: Duration, + /// Position sprite is located at + pub sprite_pos: Vec3, + /// Kind of sprite interacted with + pub sprite_kind: SpriteInteractKind, + /// Had weapon wielded + pub was_wielded: bool, + /// Was sneaking + pub was_sneak: bool, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_orientation(data, &mut update, 0.0); + handle_move(data, &mut update, 0.0); + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::SpriteInteract(Data { + timer: tick_attack_or_default(data, self.timer, None), + ..*self + }); + } else { + // Transitions to use section of stage + update.character = CharacterState::SpriteInteract(Data { + timer: Duration::default(), + stage_section: StageSection::Action, + ..*self + }); + } + }, + StageSection::Action => { + if self.timer < self.static_data.use_duration { + // Item use + update.character = CharacterState::SpriteInteract(Data { + timer: tick_attack_or_default(data, self.timer, None), + ..*self + }); + } else { + // Transitions to recover section of stage + update.character = CharacterState::SpriteInteract(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + // Recovery + update.character = CharacterState::SpriteInteract(Data { + timer: tick_attack_or_default(data, self.timer, None), + ..*self + }); + } else { + // Create inventory manipulation event + let inv_manip = InventoryManip::Collect(self.static_data.sprite_pos); + update + .server_events + .push_front(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; + } else { + update.character = CharacterState::Idle; + } + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Idle; + }, + } + + // At end of state logic so an interrupt isn't overwritten + handle_state_interrupt(data, &mut update, false); + + update + } +} + +/// Used to control effects based off of the type of item used +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum SpriteInteractKind { + Chest, + Harvestable, + Collectible, +} + +impl From for Option { + fn from(sprite_kind: SpriteKind) -> Self { + match sprite_kind { + SpriteKind::Apple + | SpriteKind::Mushroom + | SpriteKind::RedFlower + | SpriteKind::Sunflower + | SpriteKind::Coconut + | SpriteKind::Beehive + | SpriteKind::Cotton + | SpriteKind::Moonbell + | SpriteKind::Pyrebloom + | SpriteKind::WildFlax + | SpriteKind::RoundCactus + | SpriteKind::ShortFlatCactus + | SpriteKind::MedFlatCactus => Some(SpriteInteractKind::Harvestable), + SpriteKind::Stones + | SpriteKind::Twigs + | SpriteKind::VialEmpty + | SpriteKind::Bowl + | SpriteKind::PotionMinor + | SpriteKind::Seashells => Some(SpriteInteractKind::Collectible), + SpriteKind::DungeonChest0 + | SpriteKind::DungeonChest1 + | SpriteKind::DungeonChest2 + | SpriteKind::DungeonChest3 + | SpriteKind::DungeonChest4 + | SpriteKind::DungeonChest5 + | SpriteKind::Chest + | SpriteKind::ChestBuried + | SpriteKind::Mud + | SpriteKind::Crate => Some(SpriteInteractKind::Chest), + _ => None, + } + } +} + +impl SpriteInteractKind { + /// Returns (buildup, use, recover) + pub fn durations(&self) -> (Duration, Duration, Duration) { + match self { + Self::Chest => ( + Duration::from_secs_f32(0.5), + Duration::from_secs_f32(2.0), + Duration::from_secs_f32(0.5), + ), + Self::Collectible => ( + Duration::from_secs_f32(0.1), + Duration::from_secs_f32(0.3), + Duration::from_secs_f32(0.1), + ), + Self::Harvestable => ( + Duration::from_secs_f32(0.3), + Duration::from_secs_f32(0.5), + Duration::from_secs_f32(0.2), + ), + } + } +} diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index c9be9a3605..e060a68d74 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -13,6 +13,7 @@ use crate::{ event::{LocalEvent, ServerEvent}, states::{behavior::JoinData, *}, util::Dir, + vol::ReadVol, }; use serde::{Deserialize, Serialize}; use std::{ @@ -584,44 +585,87 @@ pub fn handle_manipulate_loadout( update: &mut StateUpdate, inv_action: InventoryAction, ) { - use use_item::ItemUseKind; - if let InventoryAction::Use(Slot::Inventory(inv_slot)) = inv_action { - // If inventory action is using a slot, and slot is in the inventory - // TODO: Do some non lazy way of handling the possibility that items equipped in - // the loadout will have effects that are desired to be non-instantaneous - if let Some((item_kind, item)) = data - .inventory - .and_then(|inv| inv.get(inv_slot)) - .and_then(|item| Option::::from(item.kind()).zip(Some(item))) - { - let (buildup_duration, use_duration, recover_duration) = item_kind.durations(); - // If item returns a valid kind for item use, do into use item character state - update.character = CharacterState::UseItem(use_item::Data { - static_data: use_item::StaticData { - buildup_duration, - use_duration, - recover_duration, - 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), - }, - timer: Duration::default(), - stage_section: StageSection::Buildup, - }); - } else { - // Else emit inventory action instantnaneously + match inv_action { + InventoryAction::Use(Slot::Inventory(inv_slot)) => { + // If inventory action is using a slot, and slot is in the inventory + // TODO: Do some non lazy way of handling the possibility that items equipped in + // the loadout will have effects that are desired to be non-instantaneous + use use_item::ItemUseKind; + if let Some((item_kind, item)) = data + .inventory + .and_then(|inv| inv.get(inv_slot)) + .and_then(|item| Option::::from(item.kind()).zip(Some(item))) + { + let (buildup_duration, use_duration, recover_duration) = item_kind.durations(); + // If item returns a valid kind for item use, do into use item character state + update.character = CharacterState::UseItem(use_item::Data { + static_data: use_item::StaticData { + buildup_duration, + use_duration, + recover_duration, + 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), + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }); + } else { + // Else emit inventory action instantnaneously + update + .server_events + .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + } + }, + InventoryAction::Collect(pos) => { + // First, get sprite data for position, if there is a sprite + use sprite_interact::SpriteInteractKind; + let sprite_at_pos = data + .terrain + .get(pos) + .ok() + .copied() + .and_then(|b| b.get_sprite()); + + // Checks if position has a collectible sprite as wella s what sprite is at the + // position + let sprite_interact = sprite_at_pos.and_then(Option::::from); + + if let Some(sprite_interact) = sprite_interact { + // If the sprite is collectible, enter the sprite interaction character state + // TODO: Handle cases for sprite being interactible, but not collectible (none + // currently exist) + let (buildup_duration, use_duration, recover_duration) = + sprite_interact.durations(); + + update.character = CharacterState::SpriteInteract(sprite_interact::Data { + static_data: sprite_interact::StaticData { + buildup_duration, + use_duration, + recover_duration, + sprite_pos: pos, + sprite_kind: sprite_interact, + was_wielded: matches!(data.character, CharacterState::Wielding), + was_sneak: matches!(data.character, CharacterState::Sneak), + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }) + } else { + // Otherwise send server event immediately + update + .server_events + .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + } + }, + _ => { + // Else just do event instantaneously update .server_events .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); - } - } else { - // Else if inventory action is not item use, or if slot is in loadout, just do - // event instantaneously - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + }, } } diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index e3cc55faa3..77288da764 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -261,7 +261,8 @@ impl<'a> System<'a> for Sys { | CharacterState::Climb { .. } | CharacterState::Stunned { .. } | CharacterState::BasicBlock { .. } - | CharacterState::UseItem { .. } => {}, + | CharacterState::UseItem { .. } + | CharacterState::SpriteInteract { .. } => {}, } } From 3ddcde645f5bdb472a55b9e56ca9b316fe407521 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Fri, 27 Aug 2021 02:58:09 -0400 Subject: [PATCH 02/15] anim basics --- voxygen/anim/src/character/collect.rs | 90 +++++++++++++++++++++++++++ voxygen/anim/src/character/mod.rs | 5 +- voxygen/src/scene/figure/mod.rs | 21 +++++++ 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 voxygen/anim/src/character/collect.rs diff --git a/voxygen/anim/src/character/collect.rs b/voxygen/anim/src/character/collect.rs new file mode 100644 index 0000000000..8c81c435a5 --- /dev/null +++ b/voxygen/anim/src/character/collect.rs @@ -0,0 +1,90 @@ +use super::{ + super::{vek::*, Animation}, + CharacterSkeleton, SkeletonAttr, +}; +use common::states::utils::StageSection; +use std::f32::consts::PI; + +pub struct CollectAnimation; + +impl Animation for CollectAnimation { + #[allow(clippy::type_complexity)] + type Dependency<'a> = (Vec3, f32, Option, Vec3); + type Skeleton = CharacterSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"character_collect\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "character_collect")] + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (position, _global_time, stage_section, sprite_pos): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + let (movement1, move2, move2alt, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), + Some(StageSection::Action) => ( + 1.0, + (anim_time * 12.0).sin(), + (anim_time * 9.0 + PI / 2.0).sin(), + 0.0, + ), + Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time.powi(4)), + _ => (0.0, 0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + + let move1 = movement1 * pullback; + + println!("{} pos z", position.z); + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_x(move1 * 0.2); + + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + move2 * 0.15); + next.chest.orientation = Quaternion::rotation_x(move1 * -1.0 + move2alt * 0.015); + + next.belt.position = Vec3::new(0.0, s_a.belt.0 + move1 * 1.0, s_a.belt.1 + move1 * -0.0); + next.belt.orientation = Quaternion::rotation_x(move1 * 0.2); + + next.back.position = Vec3::new(0.0, s_a.back.0, s_a.back.1); + + next.shorts.position = + Vec3::new(0.0, s_a.shorts.0 + move1 * 2.0, s_a.shorts.1 + move1 * -0.0); + next.shorts.orientation = Quaternion::rotation_x(move1 * 0.3); + + next.hand_l.position = Vec3::new( + -s_a.hand.0 + move1 * 4.0 - move2alt * 1.0, + s_a.hand.1 + move1 * 8.0 + move2 * 1.0, + s_a.hand.2 + move1 * 5.0, + ); + + next.hand_l.orientation = Quaternion::rotation_x(move1 * 1.9) + * Quaternion::rotation_y(move1 * -0.3 + move2alt * -0.2); + + next.hand_r.position = Vec3::new( + s_a.hand.0 + move1 * -4.0 - move2 * 1.0, + s_a.hand.1 + move1 * 8.0 + move2alt * -1.0, + s_a.hand.2 + move1 * 5.0, + ); + next.hand_r.orientation = + Quaternion::rotation_x(move1 * 1.9) * Quaternion::rotation_y(move1 * 0.3 + move2 * 0.3); + + next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1 + move1 * 2.0, s_a.foot.2); + next.foot_l.orientation = Quaternion::rotation_x(move1 * -0.2); + + next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1 + move1 * -4.0, s_a.foot.2); + next.foot_r.orientation = Quaternion::rotation_x(move1 * -0.8); + + next.shoulder_l.position = Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + + next.shoulder_r.position = Vec3::new(s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + + next + } +} diff --git a/voxygen/anim/src/character/mod.rs b/voxygen/anim/src/character/mod.rs index ae9641dbf0..94676c325b 100644 --- a/voxygen/anim/src/character/mod.rs +++ b/voxygen/anim/src/character/mod.rs @@ -4,6 +4,7 @@ pub mod beta; pub mod block; pub mod chargeswing; pub mod climb; +pub mod collect; pub mod consume; pub mod dance; pub mod dash; @@ -34,8 +35,8 @@ pub mod wield; // Reexports pub use self::{ alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, block::BlockAnimation, - chargeswing::ChargeswingAnimation, climb::ClimbAnimation, consume::ConsumeAnimation, - dance::DanceAnimation, dash::DashAnimation, equip::EquipAnimation, + chargeswing::ChargeswingAnimation, climb::ClimbAnimation, collect::CollectAnimation, + consume::ConsumeAnimation, dance::DanceAnimation, dash::DashAnimation, equip::EquipAnimation, glidewield::GlideWieldAnimation, gliding::GlidingAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation, mount::MountAnimation, repeater::RepeaterAnimation, roll::RollAnimation, run::RunAnimation, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 92dc046bbf..f6d85e4e47 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1133,6 +1133,27 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::SpriteInteract(s) => { + let stage_time = s.timer.as_secs_f32(); + let sprite_pos = s.static_data.sprite_pos; + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Action => s.timer.as_secs_f32(), + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::character::CollectAnimation::update_skeleton( + &target_base, + (pos.0, time, Some(s.stage_section), sprite_pos), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::Boost(_) => { anim::character::AlphaAnimation::update_skeleton( &target_base, From 513982ca430ffb409f77ca6aea97aa389904a9e7 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 28 Aug 2021 21:20:07 -0400 Subject: [PATCH 03/15] Fixed compile error when importing sprite position. --- voxygen/anim/src/character/collect.rs | 2 +- voxygen/src/scene/figure/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/voxygen/anim/src/character/collect.rs b/voxygen/anim/src/character/collect.rs index 8c81c435a5..1ede0dabfd 100644 --- a/voxygen/anim/src/character/collect.rs +++ b/voxygen/anim/src/character/collect.rs @@ -9,7 +9,7 @@ pub struct CollectAnimation; impl Animation for CollectAnimation { #[allow(clippy::type_complexity)] - type Dependency<'a> = (Vec3, f32, Option, Vec3); + type Dependency<'a> = (Vec3, f32, Option, Vec3); type Skeleton = CharacterSkeleton; #[cfg(feature = "use-dyn-lib")] diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index f6d85e4e47..39501fed8e 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1148,7 +1148,7 @@ impl FigureMgr { }; anim::character::CollectAnimation::update_skeleton( &target_base, - (pos.0, time, Some(s.stage_section), sprite_pos), + (pos.0, time, Some(s.stage_section), anim::vek::Vec3::from(sprite_pos.map(|x| x as f32))), stage_progress, &mut state_animation_rate, skeleton_attr, From 44c3b0f1537093a9f59d5dc0d27c47c349536658 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 28 Aug 2021 21:55:01 -0400 Subject: [PATCH 04/15] Added attack hook event, attacks now cancel item use and sprite interaction. --- common/src/combat.rs | 7 ++++++ common/src/event.rs | 3 +++ common/systems/src/character_behavior.rs | 12 ----------- server/src/events/entity_manipulation.rs | 27 ++++++++++++++++++++++++ server/src/events/mod.rs | 5 ++++- voxygen/src/scene/figure/mod.rs | 7 +++++- 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 82adadd44f..a1976d5246 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -452,6 +452,13 @@ impl Attack { } } } + // Emits event to handle things that should happen for any successful attack, + // regardless of if the attack had any damages or effects in it + if is_applied { + emit(ServerEvent::EntityAttackedHook { + entity: target.entity, + }); + } is_applied } } diff --git a/common/src/event.rs b/common/src/event.rs index 79ae53fbdd..843c775f89 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -197,6 +197,9 @@ pub enum ServerEvent { pet_entity: EcsEntity, owner_entity: EcsEntity, }, + EntityAttackedHook { + entity: EcsEntity, + }, } pub struct EventBus { diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index 930a243636..d78b319b7d 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -134,18 +134,6 @@ impl<'a> System<'a> for Sys { let was_wielded = char_state.is_wield(); let poise_state = poise.poise_state(); let pos = pos.0; - // Remove potion/saturation buff if knocked into poise state - if !matches!(poise_state, PoiseState::Normal) { - use comp::buff::{BuffChange, BuffKind}; - server_emitter.emit(ServerEvent::Buff { - entity, - buff_change: BuffChange::RemoveByKind(BuffKind::Potion), - }); - server_emitter.emit(ServerEvent::Buff { - entity, - buff_change: BuffChange::RemoveByKind(BuffKind::Saturation), - }); - } match poise_state { PoiseState::Normal => {}, PoiseState::Interrupted => { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 972fdf9a6c..57b3c6c302 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1230,3 +1230,30 @@ pub fn handle_teleport_to(server: &Server, entity: EcsEntity, target: Uid, max_r } } } + +/// Intended to handle things that should happen for any successful attack, +/// regardless of the damages and effects specific to that attack +pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) { + let ecs = &server.state.ecs(); + let server_eventbus = ecs.read_resource::>(); + + if let Some(mut char_state) = ecs.write_storage::().get_mut(entity) { + // Interrupt sprite interaction and item use if any attack is applied to entity + if matches!( + *char_state, + CharacterState::SpriteInteract(_) | CharacterState::UseItem(_) + ) { + *char_state = CharacterState::Idle; + } + } + + // Remove potion/saturation buff if attacked + server_eventbus.emit_now(ServerEvent::Buff { + entity, + buff_change: buff::BuffChange::RemoveByKind(buff::BuffKind::Potion), + }); + server_eventbus.emit_now(ServerEvent::Buff { + entity, + buff_change: buff::BuffChange::RemoveByKind(buff::BuffKind::Saturation), + }); +} diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index ee78736acc..1fce3b2b8c 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -7,7 +7,7 @@ use entity_creation::{ }; use entity_manipulation::{ handle_aura, handle_bonk, handle_buff, handle_combo_change, handle_damage, handle_delete, - handle_destroy, handle_energy_change, handle_explosion, handle_knockback, + handle_destroy, handle_energy_change, handle_entity_attacked_hook, handle_explosion, handle_knockback, handle_land_on_ground, handle_poise, handle_respawn, handle_teleport_to, }; use group_manip::handle_group; @@ -229,6 +229,9 @@ impl Server { pet_entity, owner_entity, } => handle_tame_pet(self, pet_entity, owner_entity), + ServerEvent::EntityAttackedHook { entity } => { + handle_entity_attacked_hook(self, entity) + }, } } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 39501fed8e..12e6d8b15f 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1148,7 +1148,12 @@ impl FigureMgr { }; anim::character::CollectAnimation::update_skeleton( &target_base, - (pos.0, time, Some(s.stage_section), anim::vek::Vec3::from(sprite_pos.map(|x| x as f32))), + ( + pos.0, + time, + Some(s.stage_section), + anim::vek::Vec3::from(sprite_pos.map(|x| x as f32)), + ), stage_progress, &mut state_animation_rate, skeleton_attr, From 42a0dd785bc9f628652e3ac16df597c887bcfae5 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Sun, 29 Aug 2021 03:52:56 -0400 Subject: [PATCH 05/15] finished anim --- voxygen/anim/src/character/collect.rs | 64 ++++++++++++++++----------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/voxygen/anim/src/character/collect.rs b/voxygen/anim/src/character/collect.rs index 1ede0dabfd..92fec8ef2c 100644 --- a/voxygen/anim/src/character/collect.rs +++ b/voxygen/anim/src/character/collect.rs @@ -38,15 +38,26 @@ impl Animation for CollectAnimation { Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0, 0.0), }; + let z_diff = (sprite_pos.z - position.z).round(); + let z_diff = if z_diff > 0.0 { z_diff / 9.0 } else { 0.0 }; + let squat = (1.0 - z_diff).powf(4.0); + let pullback = 1.0 - move3; - let move1 = movement1 * pullback; + let move1 = movement1 * pullback * squat; + let move1_nosquat = movement1 * pullback; + let upshift = if squat < 0.35 { + move1_nosquat * 0.3 + } else { + 0.0 + }; + next.head.orientation = Quaternion::rotation_x(move1_nosquat * 0.2 + upshift * 1.3); - println!("{} pos z", position.z); - next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); - next.head.orientation = Quaternion::rotation_x(move1 * 0.2); - - next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + move2 * 0.15); + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0 + upshift * 3.0, + s_a.chest.1 + move2 * 0.15 + upshift * 3.0, + ); next.chest.orientation = Quaternion::rotation_x(move1 * -1.0 + move2alt * 0.015); next.belt.position = Vec3::new(0.0, s_a.belt.0 + move1 * 1.0, s_a.belt.1 + move1 * -0.0); @@ -59,32 +70,35 @@ impl Animation for CollectAnimation { next.shorts.orientation = Quaternion::rotation_x(move1 * 0.3); next.hand_l.position = Vec3::new( - -s_a.hand.0 + move1 * 4.0 - move2alt * 1.0, - s_a.hand.1 + move1 * 8.0 + move2 * 1.0, - s_a.hand.2 + move1 * 5.0, + -s_a.hand.0 + move1_nosquat * 4.0 - move2alt * 1.0, + s_a.hand.1 + move1_nosquat * 8.0 + move2 * 1.0 + upshift * -5.0, + s_a.hand.2 + move1_nosquat * 5.0 + upshift * 15.0, ); - next.hand_l.orientation = Quaternion::rotation_x(move1 * 1.9) - * Quaternion::rotation_y(move1 * -0.3 + move2alt * -0.2); + next.hand_l.orientation = Quaternion::rotation_x(move1_nosquat * 1.9 + upshift * 2.0) + * Quaternion::rotation_y(move1_nosquat * -0.3 + move2alt * -0.2); next.hand_r.position = Vec3::new( - s_a.hand.0 + move1 * -4.0 - move2 * 1.0, - s_a.hand.1 + move1 * 8.0 + move2alt * -1.0, - s_a.hand.2 + move1 * 5.0, + s_a.hand.0 + move1_nosquat * -4.0 - move2 * 1.0, + s_a.hand.1 + move1_nosquat * 8.0 + move2alt * -1.0 + upshift * -5.0, + s_a.hand.2 + move1_nosquat * 5.0 + upshift * 15.0, ); - next.hand_r.orientation = - Quaternion::rotation_x(move1 * 1.9) * Quaternion::rotation_y(move1 * 0.3 + move2 * 0.3); + next.hand_r.orientation = Quaternion::rotation_x(move1_nosquat * 1.9 + upshift * 2.0) + * Quaternion::rotation_y(move1_nosquat * 0.3 + move2 * 0.3); - next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1 + move1 * 2.0, s_a.foot.2); - next.foot_l.orientation = Quaternion::rotation_x(move1 * -0.2); - - next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1 + move1 * -4.0, s_a.foot.2); - next.foot_r.orientation = Quaternion::rotation_x(move1 * -0.8); - - next.shoulder_l.position = Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); - - next.shoulder_r.position = Vec3::new(s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + move1 * 2.0 + upshift * -3.5, + s_a.foot.2 + upshift * 2.0, + ); + next.foot_l.orientation = Quaternion::rotation_x(move1 * -0.2 + upshift * -2.2); + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + move1 * -4.0 + upshift * -0.5, + s_a.foot.2 + upshift * 2.0, + ); + next.foot_r.orientation = Quaternion::rotation_x(move1 * -0.8 + upshift * -1.2); next } } From 9e5744f3eef98bd4d62eb4f21bd849a6c0c85a59 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 29 Aug 2021 19:23:34 -0400 Subject: [PATCH 06/15] Entities will now attempt to orient towards the sprite they are interacting with. --- common/src/states/basic_aura.rs | 2 +- common/src/states/basic_beam.rs | 2 +- common/src/states/basic_block.rs | 2 +- common/src/states/basic_melee.rs | 3 +-- common/src/states/basic_ranged.rs | 2 +- common/src/states/blink.rs | 2 +- common/src/states/charged_melee.rs | 2 +- common/src/states/charged_ranged.rs | 2 +- common/src/states/combo_melee.rs | 21 ++++++++++++++++++--- common/src/states/dash_melee.rs | 4 ++-- common/src/states/equipping.rs | 2 +- common/src/states/glide_wield.rs | 2 +- common/src/states/idle.rs | 2 +- common/src/states/leap_melee.rs | 2 +- common/src/states/repeater_ranged.rs | 2 +- common/src/states/roll.rs | 2 +- common/src/states/shockwave.rs | 2 +- common/src/states/sneak.rs | 2 +- common/src/states/sprite_interact.rs | 6 +++++- common/src/states/stunned.rs | 2 +- common/src/states/talk.rs | 2 +- common/src/states/use_item.rs | 4 ++-- common/src/states/utils.rs | 19 +++++++++++++------ common/src/states/wielding.rs | 2 +- 24 files changed, 59 insertions(+), 34 deletions(-) diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs index 6f7b9c9dc8..0c2f084c67 100644 --- a/common/src/states/basic_aura.rs +++ b/common/src/states/basic_aura.rs @@ -55,7 +55,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.8); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index f7f72720be..751cc105ed 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -67,7 +67,7 @@ impl CharacterBehavior for Data { let ori_rate = self.static_data.ori_rate; - handle_orientation(data, &mut update, ori_rate); + handle_orientation(data, &mut update, ori_rate, None); handle_move(data, &mut update, 0.4); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index 298fc2206e..aca9b9edec 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -36,7 +36,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.4); match self.stage_section { diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index e32ccc5225..3e83562c6e 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -56,10 +56,9 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); handle_jump(data, &mut update, 1.0); - handle_orientation(data, &mut update, 0.35); match self.stage_section { StageSection::Buildup => { diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 7ca246f7c4..cf2bb06523 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -48,7 +48,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.3); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/blink.rs b/common/src/states/blink.rs index 66bd9b14c7..fae3bbef76 100644 --- a/common/src/states/blink.rs +++ b/common/src/states/blink.rs @@ -37,7 +37,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); match self.stage_section { StageSection::Buildup => { diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 37691a5907..f53e726466 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -72,7 +72,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.7); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index bd9aabd054..9ef1c430ef 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -74,7 +74,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, self.static_data.move_speed); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index affcdfa5e9..1dc7d2f585 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -175,7 +175,12 @@ impl CharacterBehavior for Data { match self.stage_section { StageSection::Buildup => { if self.timer < self.static_data.stage_data[stage_index].base_buildup_duration { - handle_orientation(data, &mut update, 0.4 * self.static_data.ori_modifier); + handle_orientation( + data, + &mut update, + 0.4 * self.static_data.ori_modifier, + None, + ); // Build up update.character = CharacterState::ComboMelee(Data { @@ -289,7 +294,12 @@ impl CharacterBehavior for Data { }); } else if self.timer < self.static_data.stage_data[stage_index].base_swing_duration { - handle_orientation(data, &mut update, 0.4 * self.static_data.ori_modifier); + handle_orientation( + data, + &mut update, + 0.4 * self.static_data.ori_modifier, + None, + ); // Forward movement handle_forced_movement(data, &mut update, ForcedMovement::Forward { @@ -314,7 +324,12 @@ impl CharacterBehavior for Data { }, StageSection::Recover => { if self.timer < self.static_data.stage_data[stage_index].base_recover_duration { - handle_orientation(data, &mut update, 0.8 * self.static_data.ori_modifier); + handle_orientation( + data, + &mut update, + 0.8 * self.static_data.ori_modifier, + None, + ); // Recovers update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index fe086ec68e..6deed4ac07 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -82,7 +82,7 @@ impl CharacterBehavior for Data { match self.stage_section { StageSection::Buildup => { if self.timer < self.static_data.buildup_duration { - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); // Build up update.character = CharacterState::DashMelee(Data { timer: tick_attack_or_default(data, self.timer, None), @@ -109,7 +109,7 @@ impl CharacterBehavior for Data { / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - handle_orientation(data, &mut update, self.static_data.ori_modifier); + handle_orientation(data, &mut update, self.static_data.ori_modifier, None); handle_forced_movement(data, &mut update, ForcedMovement::Forward { strength: self.static_data.forward_speed * charge_frac.sqrt(), }); diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index fc531dab62..609acf48ae 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -26,7 +26,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index ade2f71b58..88da23275b 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -35,7 +35,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_jump(data, &mut update, 1.0); handle_dodge_input(data, &mut update); diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index 1834677354..d1f02390b1 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -10,7 +10,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_jump(data, &mut update, 1.0); handle_wield(data, &mut update); diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 8125ec4617..dfcdc6ce67 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -60,7 +60,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.3); handle_jump(data, &mut update, 1.0); diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index de55813305..07471bcd25 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -54,7 +54,7 @@ pub struct Data { impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.3); match self.stage_section { diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 802da371e1..d3c593bfd7 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -54,7 +54,7 @@ impl CharacterBehavior for Data { update.should_strafe = false; // Smooth orientation - handle_orientation(data, &mut update, 2.5); + handle_orientation(data, &mut update, 2.5, None); match self.stage_section { StageSection::Buildup => { diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index 440fc4f834..70d3eee546 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -65,7 +65,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, self.static_data.move_efficiency); match self.stage_section { diff --git a/common/src/states/sneak.rs b/common/src/states/sneak.rs index bc4c6566ba..537c7a9836 100644 --- a/common/src/states/sneak.rs +++ b/common/src/states/sneak.rs @@ -10,7 +10,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 0.4); handle_jump(data, &mut update, 1.0); handle_wield(data, &mut update); diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 2d7570a881..2b2ee9b1f9 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -4,6 +4,7 @@ use crate::{ event::ServerEvent, states::behavior::{CharacterBehavior, JoinData}, terrain::SpriteKind, + util::Dir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -43,7 +44,10 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 0.0); + let ori_dir = Dir::from_unnormalized(Vec3::from( + (self.static_data.sprite_pos.map(|x| x as f32) - data.pos.0).xy(), + )); + handle_orientation(data, &mut update, 1.0, ori_dir); handle_move(data, &mut update, 0.0); match self.stage_section { diff --git a/common/src/states/stunned.rs b/common/src/states/stunned.rs index 9d169e01dc..97bd5e7937 100644 --- a/common/src/states/stunned.rs +++ b/common/src/states/stunned.rs @@ -36,7 +36,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, self.static_data.movement_speed); match self.stage_section { diff --git a/common/src/states/talk.rs b/common/src/states/talk.rs index 21b15dd129..59abfc8e69 100644 --- a/common/src/states/talk.rs +++ b/common/src/states/talk.rs @@ -15,7 +15,7 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); handle_wield(data, &mut update); - handle_orientation(data, &mut update, TURN_RATE); + handle_orientation(data, &mut update, TURN_RATE, None); update } diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index 0265c5b9f6..10d490eec2 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -52,11 +52,11 @@ impl CharacterBehavior for Data { match self.static_data.item_kind { ItemUseKind::Consumable(ConsumableKind::Drink) => { - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); }, ItemUseKind::Consumable(ConsumableKind::Food | ConsumableKind::ComplexFood) => { - handle_orientation(data, &mut update, 0.0); + handle_orientation(data, &mut update, 0.0, None); handle_move(data, &mut update, 0.0); }, } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index e060a68d74..c4e8baa05d 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -349,11 +349,18 @@ pub fn handle_forced_movement( } } -pub fn handle_orientation(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) { - let dir = (is_strafing(data, update) || update.character.is_attack()) - .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) - .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) - .unwrap_or_else(|| data.ori.to_horizontal().look_dir()); +pub fn handle_orientation( + data: &JoinData<'_>, + update: &mut StateUpdate, + efficiency: f32, + dir_override: Option, +) { + let dir = dir_override.unwrap_or_else(|| + (is_strafing(data, update) || update.character.is_attack()) + .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) + .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) + .unwrap_or_else(|| data.ori.to_horizontal().look_dir()), + ); let rate = { let angle = update.ori.look_dir().angle_between(*dir); data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle @@ -422,7 +429,7 @@ pub fn fly_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) let thrust = efficiency * force; let accel = thrust / data.mass.0; - handle_orientation(data, update, efficiency); + handle_orientation(data, update, efficiency, None); // Elevation control match data.body { diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index 81a0bc860c..68c90aa4fd 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -13,7 +13,7 @@ impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); - handle_orientation(data, &mut update, 1.0); + handle_orientation(data, &mut update, 1.0, None); handle_move(data, &mut update, 1.0); handle_climb(data, &mut update); attempt_input(data, &mut update); From 28d23c89edbe3a59fc87258d2657ee5bf5cf664f Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 29 Aug 2021 19:46:36 -0400 Subject: [PATCH 07/15] Added simple distance check for sprite interaction. --- common/src/consts.rs | 2 +- common/src/states/utils.rs | 91 +++++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/common/src/consts.rs b/common/src/consts.rs index 4cbaa9598e..19b9b8888a 100644 --- a/common/src/consts.rs +++ b/common/src/consts.rs @@ -1,5 +1,5 @@ // The limit on distance between the entity and a collectible (squared) -pub const MAX_PICKUP_RANGE: f32 = 8.0; +pub const MAX_PICKUP_RANGE: f32 = 5.0; pub const MAX_MOUNT_RANGE: f32 = 14.0; pub const GRAVITY: f32 = 25.0; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index c4e8baa05d..6bdd3ad666 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -9,7 +9,7 @@ use crate::{ theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind, InventoryAction, StateUpdate, }, - consts::{FRIC_GROUND, GRAVITY}, + consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE}, event::{LocalEvent, ServerEvent}, states::{behavior::JoinData, *}, util::Dir, @@ -355,12 +355,12 @@ pub fn handle_orientation( efficiency: f32, dir_override: Option, ) { - let dir = dir_override.unwrap_or_else(|| + let dir = dir_override.unwrap_or_else(|| { (is_strafing(data, update) || update.character.is_attack()) .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) - .unwrap_or_else(|| data.ori.to_horizontal().look_dir()), - ); + .unwrap_or_else(|| data.ori.to_horizontal().look_dir()) + }); let rate = { let angle = update.ori.look_dir().angle_between(*dir); data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle @@ -626,45 +626,56 @@ pub fn handle_manipulate_loadout( .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); } }, - InventoryAction::Collect(pos) => { - // First, get sprite data for position, if there is a sprite - use sprite_interact::SpriteInteractKind; - let sprite_at_pos = data - .terrain - .get(pos) - .ok() - .copied() - .and_then(|b| b.get_sprite()); + InventoryAction::Collect(sprite_pos) => { + // CLosure to check if distance between a point and the sprite is less than + // MAX_PICKUP_RANGE + let sprite_range_check = |pos: Vec3| { + (sprite_pos.map(|x| x as f32) - pos).magnitude_squared() + < MAX_PICKUP_RANGE.powi(2) + }; + // Checks if player's feet or head is near to sprite + let close_to_sprite = sprite_range_check(data.pos.0) + || sprite_range_check(data.pos.0 + Vec3::new(0.0, 0.0, data.body.height())); + if close_to_sprite { + // First, get sprite data for position, if there is a sprite + use sprite_interact::SpriteInteractKind; + let sprite_at_pos = data + .terrain + .get(sprite_pos) + .ok() + .copied() + .and_then(|b| b.get_sprite()); - // Checks if position has a collectible sprite as wella s what sprite is at the - // position - let sprite_interact = sprite_at_pos.and_then(Option::::from); + // Checks if position has a collectible sprite as wella s what sprite is at the + // position + let sprite_interact = sprite_at_pos.and_then(Option::::from); - if let Some(sprite_interact) = sprite_interact { - // If the sprite is collectible, enter the sprite interaction character state - // TODO: Handle cases for sprite being interactible, but not collectible (none - // currently exist) - let (buildup_duration, use_duration, recover_duration) = - sprite_interact.durations(); + if let Some(sprite_interact) = sprite_interact { + // If the sprite is collectible, enter the sprite interaction character state + // TODO: Handle cases for sprite being interactible, but not collectible (none + // currently exist) + let (buildup_duration, use_duration, recover_duration) = + sprite_interact.durations(); - update.character = CharacterState::SpriteInteract(sprite_interact::Data { - static_data: sprite_interact::StaticData { - buildup_duration, - use_duration, - recover_duration, - sprite_pos: pos, - sprite_kind: sprite_interact, - was_wielded: matches!(data.character, CharacterState::Wielding), - was_sneak: matches!(data.character, CharacterState::Sneak), - }, - timer: Duration::default(), - stage_section: StageSection::Buildup, - }) - } else { - // Otherwise send server event immediately - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + update.character = CharacterState::SpriteInteract(sprite_interact::Data { + static_data: sprite_interact::StaticData { + buildup_duration, + use_duration, + recover_duration, + sprite_pos, + sprite_kind: sprite_interact, + was_wielded: matches!(data.character, CharacterState::Wielding), + was_sneak: matches!(data.character, CharacterState::Sneak), + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }) + } else { + // Otherwise send server event immediately + update + .server_events + .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); + } } }, _ => { From 6d91bae44b994c92aea4ffe055fe49996e43ee38 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 29 Aug 2021 21:50:26 -0400 Subject: [PATCH 08/15] Sprite interaction now uses pathfinding to check that it can be picked up. --- Cargo.lock | 1 + common/Cargo.toml | 1 + common/src/states/utils.rs | 54 +++++++++++++++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3fc723502..7cdac5b797 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5859,6 +5859,7 @@ dependencies = [ "csv", "dot_vox", "enum-iterator", + "fxhash", "hashbrown 0.11.2", "indexmap", "lazy_static", diff --git a/common/Cargo.toml b/common/Cargo.toml index 9384818e12..fed28470a3 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -47,6 +47,7 @@ spin_sleep = "1.0" tracing = { version = "0.1", default-features = false } uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] } rand = "0.8" +fxhash = "0.2.1" # Assets common-assets = {package = "veloren-common-assets", path = "assets"} diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 6bdd3ad666..0c0f408d8b 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,4 +1,5 @@ use crate::{ + astar::Astar, combat, comp::{ biped_large, biped_small, @@ -15,6 +16,8 @@ use crate::{ util::Dir, vol::ReadVol, }; +use core::hash::BuildHasherDefault; +use fxhash::FxHasher64; use serde::{Deserialize, Serialize}; use std::{ ops::{Add, Div}, @@ -627,16 +630,61 @@ pub fn handle_manipulate_loadout( } }, InventoryAction::Collect(sprite_pos) => { + let sprite_pos_f32 = sprite_pos.map(|x| x as f32); // CLosure to check if distance between a point and the sprite is less than // MAX_PICKUP_RANGE let sprite_range_check = |pos: Vec3| { - (sprite_pos.map(|x| x as f32) - pos).magnitude_squared() - < MAX_PICKUP_RANGE.powi(2) + (sprite_pos_f32 - pos).magnitude_squared() < MAX_PICKUP_RANGE.powi(2) }; + + // Also do a check that a path can be found between sprite and entity + // interacting with sprite Use manhattan distance * 1.5 for number + // of iterations + let iters = (2.0 * (sprite_pos_f32 - data.pos.0).map(|x| x.abs()).sum()) as usize; + // Heuristic compares manhattan distance of start and end pos + let heuristic = move |pos: &Vec3| (sprite_pos - pos).map(|x| x.abs()).sum() as f32; + + let mut astar = Astar::new( + iters, + data.pos.0.map(|x| x.floor() as i32), + heuristic, + BuildHasherDefault::::default(), + ); + + // Neighbors are all neighboring blocks that are air + let neighbors = |pos: &Vec3| { + const DIRS: [Vec3; 6] = [ + Vec3::new(1, 0, 0), + Vec3::new(-1, 0, 0), + Vec3::new(0, 1, 0), + Vec3::new(0, -1, 0), + Vec3::new(0, 0, 1), + Vec3::new(0, 0, -1), + ]; + let pos = *pos; + DIRS.iter() + .map(move |dir| dir + pos) + .filter_map(move |pos| { + data.terrain + .get(pos) + .ok() + .and_then(|block| block.is_air().then_some(pos)) + }) + }; + // Transition uses manhattan distance as the cost + let transition = |_: &Vec3, _: &Vec3| 1.0; + // Pathing satisfied when it reaches the sprite position + let satisfied = |pos: &Vec3| *pos == sprite_pos; + + let not_blocked_by_terrain = astar + .poll(iters, heuristic, neighbors, transition, satisfied) + .into_path() + .is_some(); + // Checks if player's feet or head is near to sprite let close_to_sprite = sprite_range_check(data.pos.0) || sprite_range_check(data.pos.0 + Vec3::new(0.0, 0.0, data.body.height())); - if close_to_sprite { + if close_to_sprite && not_blocked_by_terrain { // First, get sprite data for position, if there is a sprite use sprite_interact::SpriteInteractKind; let sprite_at_pos = data From 13fa154e55296f43cea5efadf9d26aae703b28b1 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 29 Aug 2021 22:06:05 -0400 Subject: [PATCH 09/15] Changelog and added arm length (body radius) to sprite pickup range. --- CHANGELOG.md | 2 ++ common/src/states/utils.rs | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff9424baef..2ab40c8844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The 'spot' system, which generates smaller site-like structures and scenarios - Chestnut and cedar tree varieties - Shooting sprites, such as apples and hives, can knock them out of trees +- Sprite pickup animations ### Changed @@ -66,6 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Enemies no more spawn in dungeon boss room - Melee critical hit no more applies after reduction by armour - Removed Healing Sceptre as a starting weapon as it is considered an advanced weapon +- The ability to pickup sprites through walls ### Fixed diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 0c0f408d8b..f3899bfb7f 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -632,15 +632,16 @@ pub fn handle_manipulate_loadout( InventoryAction::Collect(sprite_pos) => { let sprite_pos_f32 = sprite_pos.map(|x| x as f32); // CLosure to check if distance between a point and the sprite is less than - // MAX_PICKUP_RANGE + // MAX_PICKUP_RANGE and the radius of the body let sprite_range_check = |pos: Vec3| { - (sprite_pos_f32 - pos).magnitude_squared() < MAX_PICKUP_RANGE.powi(2) + (sprite_pos_f32 - pos).magnitude_squared() + < (MAX_PICKUP_RANGE + data.body.radius()).powi(2) }; // Also do a check that a path can be found between sprite and entity // interacting with sprite Use manhattan distance * 1.5 for number // of iterations - let iters = (2.0 * (sprite_pos_f32 - data.pos.0).map(|x| x.abs()).sum()) as usize; + let iters = (3.0 * (sprite_pos_f32 - data.pos.0).map(|x| x.abs()).sum()) as usize; // Heuristic compares manhattan distance of start and end pos let heuristic = move |pos: &Vec3| (sprite_pos - pos).map(|x| x.abs()).sum() as f32; From 6b71133c50a07834a96dc3c1b2740b9ef3c1f9aa Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Aug 2021 11:49:55 -0400 Subject: [PATCH 10/15] Fixed interaction to work underwater. --- common/src/states/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index f3899bfb7f..58f71791ec 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -669,7 +669,7 @@ pub fn handle_manipulate_loadout( data.terrain .get(pos) .ok() - .and_then(|block| block.is_air().then_some(pos)) + .and_then(|block| (!block.is_filled()).then_some(pos)) }) }; // Transition uses manhattan distance as the cost From c66e1b3a563f6e12934d157b509501051ceeb64e Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Aug 2021 12:28:32 -0400 Subject: [PATCH 11/15] Removed distance check from sprite colelction event handling as it is already checked when entering state. --- server/src/events/inventory_manip.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index d4e54792a6..5795f65509 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -188,21 +188,6 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv if let Some(block) = block { if block.is_collectible() && state.can_set_block(pos) { - // Check if the block is within pickup range - let entity_cylinder = get_cylinder(state, entity); - if !within_pickup_range(entity_cylinder, || { - Some(find_dist::Cube { - min: pos.as_(), - side_length: 1.0, - }) - }) { - debug!( - ?entity_cylinder, - "Failed to pick up block as not within range, block pos: {}", pos - ); - return; - }; - if let Some(item) = comp::Item::try_reclaim_from_block(block) { // NOTE: We dup the item for message purposes. let item_msg = item.duplicate( From 2a3cc3d35ac0dbf6bab8de88d46066fed142fda9 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Aug 2021 12:33:42 -0400 Subject: [PATCH 12/15] No longer send collect inventory event if interactable sprite kind cannot be found. --- common/src/states/utils.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 58f71791ec..ec6b9aaa0c 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -695,7 +695,7 @@ pub fn handle_manipulate_loadout( .copied() .and_then(|b| b.get_sprite()); - // Checks if position has a collectible sprite as wella s what sprite is at the + // Checks if position has a collectible sprite as well as what sprite is at the // position let sprite_interact = sprite_at_pos.and_then(Option::::from); @@ -719,11 +719,6 @@ pub fn handle_manipulate_loadout( timer: Duration::default(), stage_section: StageSection::Buildup, }) - } else { - // Otherwise send server event immediately - update - .server_events - .push_front(ServerEvent::InventoryManip(data.entity, inv_action.into())); } } }, From f986176604ea9666c9ecfe252ca0b01eefd69a8c Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Aug 2021 12:39:11 -0400 Subject: [PATCH 13/15] Sprites that are colelctible but not explciitly added to SpriteInteractKind match statement have a default value to allow collection. --- common/src/states/sprite_interact.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 2b2ee9b1f9..6a923f57fe 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -159,6 +159,7 @@ impl From for Option { | SpriteKind::ChestBuried | SpriteKind::Mud | SpriteKind::Crate => Some(SpriteInteractKind::Chest), + _ if sprite_kind.is_collectible() => Some(SpriteInteractKind::Collectible), _ => None, } } From 5dbc05730dee738a0210231239f3b0eb2d1dd515 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 31 Aug 2021 13:35:46 -0400 Subject: [PATCH 14/15] Moved pathfinding check to after distance check. Added a clear fallback for cases where sprite is not added to match statement. --- common/src/states/sprite_interact.rs | 18 ++-- common/src/states/utils.rs | 147 ++++++++++++++------------- 2 files changed, 91 insertions(+), 74 deletions(-) diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 6a923f57fe..21e1bc59f5 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -13,11 +13,11 @@ use vek::Vec3; /// Separated out to condense update portions of character state #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct StaticData { - /// Buildup to item use + /// Buildup to sprite interaction pub buildup_duration: Duration, - /// Duration of item use + /// Duration of sprite interaction pub use_duration: Duration, - /// Recovery after item use + /// Recovery after sprite interaction pub recover_duration: Duration, /// Position sprite is located at pub sprite_pos: Vec3, @@ -69,7 +69,7 @@ impl CharacterBehavior for Data { }, StageSection::Action => { if self.timer < self.static_data.use_duration { - // Item use + // sprite interaction update.character = CharacterState::SpriteInteract(Data { timer: tick_attack_or_default(data, self.timer, None), ..*self @@ -119,12 +119,13 @@ impl CharacterBehavior for Data { } } -/// Used to control effects based off of the type of item used +/// Used to control effects based off of the type of sprite interacted with #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum SpriteInteractKind { Chest, Harvestable, Collectible, + Fallback, } impl From for Option { @@ -159,7 +160,7 @@ impl From for Option { | SpriteKind::ChestBuried | SpriteKind::Mud | SpriteKind::Crate => Some(SpriteInteractKind::Chest), - _ if sprite_kind.is_collectible() => Some(SpriteInteractKind::Collectible), + _ if sprite_kind.is_collectible() => Some(SpriteInteractKind::Fallback), _ => None, } } @@ -184,6 +185,11 @@ impl SpriteInteractKind { Duration::from_secs_f32(0.5), Duration::from_secs_f32(0.2), ), + Self::Fallback => ( + Duration::from_secs_f32(5.0), + Duration::from_secs_f32(5.0), + Duration::from_secs_f32(5.0), + ), } } } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index ec6b9aaa0c..3db6041b6e 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -358,12 +358,17 @@ pub fn handle_orientation( efficiency: f32, dir_override: Option, ) { - let dir = dir_override.unwrap_or_else(|| { - (is_strafing(data, update) || update.character.is_attack()) - .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) - .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) + // Direction is set to the override if one is provided, else if entity is + // strafing or attacking the horiontal component of the look direction is used, + // else the current horizontal movement direction is used + let dir = if let Some(dir_override) = dir_override { + dir_override + } else if is_strafing(data, update) || update.character.is_attack() { + data.inputs.look_dir.to_horizontal().unwrap_or_default() + } else { + Dir::from_unnormalized(data.inputs.move_dir.into()) .unwrap_or_else(|| data.ori.to_horizontal().look_dir()) - }); + }; let rate = { let angle = update.ori.look_dir().angle_between(*dir); data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle @@ -638,54 +643,10 @@ pub fn handle_manipulate_loadout( < (MAX_PICKUP_RANGE + data.body.radius()).powi(2) }; - // Also do a check that a path can be found between sprite and entity - // interacting with sprite Use manhattan distance * 1.5 for number - // of iterations - let iters = (3.0 * (sprite_pos_f32 - data.pos.0).map(|x| x.abs()).sum()) as usize; - // Heuristic compares manhattan distance of start and end pos - let heuristic = move |pos: &Vec3| (sprite_pos - pos).map(|x| x.abs()).sum() as f32; - - let mut astar = Astar::new( - iters, - data.pos.0.map(|x| x.floor() as i32), - heuristic, - BuildHasherDefault::::default(), - ); - - // Neighbors are all neighboring blocks that are air - let neighbors = |pos: &Vec3| { - const DIRS: [Vec3; 6] = [ - Vec3::new(1, 0, 0), - Vec3::new(-1, 0, 0), - Vec3::new(0, 1, 0), - Vec3::new(0, -1, 0), - Vec3::new(0, 0, 1), - Vec3::new(0, 0, -1), - ]; - let pos = *pos; - DIRS.iter() - .map(move |dir| dir + pos) - .filter_map(move |pos| { - data.terrain - .get(pos) - .ok() - .and_then(|block| (!block.is_filled()).then_some(pos)) - }) - }; - // Transition uses manhattan distance as the cost - let transition = |_: &Vec3, _: &Vec3| 1.0; - // Pathing satisfied when it reaches the sprite position - let satisfied = |pos: &Vec3| *pos == sprite_pos; - - let not_blocked_by_terrain = astar - .poll(iters, heuristic, neighbors, transition, satisfied) - .into_path() - .is_some(); - // Checks if player's feet or head is near to sprite let close_to_sprite = sprite_range_check(data.pos.0) || sprite_range_check(data.pos.0 + Vec3::new(0.0, 0.0, data.body.height())); - if close_to_sprite && not_blocked_by_terrain { + if close_to_sprite { // First, get sprite data for position, if there is a sprite use sprite_interact::SpriteInteractKind; let sprite_at_pos = data @@ -700,25 +661,75 @@ pub fn handle_manipulate_loadout( let sprite_interact = sprite_at_pos.and_then(Option::::from); if let Some(sprite_interact) = sprite_interact { - // If the sprite is collectible, enter the sprite interaction character state - // TODO: Handle cases for sprite being interactible, but not collectible (none - // currently exist) - let (buildup_duration, use_duration, recover_duration) = - sprite_interact.durations(); + // Do a check that a path can be found between sprite and entity + // interacting with sprite Use manhattan distance * 1.5 for number + // of iterations + let iters = + (3.0 * (sprite_pos_f32 - data.pos.0).map(|x| x.abs()).sum()) as usize; + // Heuristic compares manhattan distance of start and end pos + let heuristic = + move |pos: &Vec3| (sprite_pos - pos).map(|x| x.abs()).sum() as f32; - update.character = CharacterState::SpriteInteract(sprite_interact::Data { - static_data: sprite_interact::StaticData { - buildup_duration, - use_duration, - recover_duration, - sprite_pos, - sprite_kind: sprite_interact, - was_wielded: matches!(data.character, CharacterState::Wielding), - was_sneak: matches!(data.character, CharacterState::Sneak), - }, - timer: Duration::default(), - stage_section: StageSection::Buildup, - }) + let mut astar = Astar::new( + iters, + data.pos.0.map(|x| x.floor() as i32), + heuristic, + BuildHasherDefault::::default(), + ); + + // Neighbors are all neighboring blocks that are air + let neighbors = |pos: &Vec3| { + const DIRS: [Vec3; 6] = [ + Vec3::new(1, 0, 0), + Vec3::new(-1, 0, 0), + Vec3::new(0, 1, 0), + Vec3::new(0, -1, 0), + Vec3::new(0, 0, 1), + Vec3::new(0, 0, -1), + ]; + let pos = *pos; + DIRS.iter().map(move |dir| dir + pos).filter(|pos| { + data.terrain + .get(*pos) + .ok() + .map_or(false, |block| !block.is_filled()) + }) + }; + // Transition uses manhattan distance as the cost, which is always 1 since we + // only ever step one block at a time + let transition = |_: &Vec3, _: &Vec3| 1.0; + // Pathing satisfied when it reaches the sprite position + let satisfied = |pos: &Vec3| *pos == sprite_pos; + + let not_blocked_by_terrain = astar + .poll(iters, heuristic, neighbors, transition, satisfied) + .into_path() + .is_some(); + + // If path can be found between entity interacting with sprite and entity, start + // interaction with sprite + if not_blocked_by_terrain { + // If the sprite is collectible, enter the sprite interaction character + // state TODO: Handle cases for sprite being + // interactible, but not collectible (none currently + // exist) + let (buildup_duration, use_duration, recover_duration) = + sprite_interact.durations(); + + update.character = CharacterState::SpriteInteract(sprite_interact::Data { + static_data: sprite_interact::StaticData { + buildup_duration, + use_duration, + recover_duration, + sprite_pos, + sprite_kind: sprite_interact, + was_wielded: matches!(data.character, CharacterState::Wielding), + was_sneak: matches!(data.character, CharacterState::Sneak), + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }) + } } } }, From 156820cd5c98a050f50bbd79891a2b4dee604d93 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 31 Aug 2021 13:57:17 -0400 Subject: [PATCH 15/15] Chest sprite interact kind changed to only check that sprite is a container rather than matching on every 'chest-like' sprite. --- common/src/states/sprite_interact.rs | 15 +++++---------- server/src/events/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 21e1bc59f5..6fc28d1173 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -150,16 +150,11 @@ impl From for Option { | SpriteKind::Bowl | SpriteKind::PotionMinor | SpriteKind::Seashells => Some(SpriteInteractKind::Collectible), - SpriteKind::DungeonChest0 - | SpriteKind::DungeonChest1 - | SpriteKind::DungeonChest2 - | SpriteKind::DungeonChest3 - | SpriteKind::DungeonChest4 - | SpriteKind::DungeonChest5 - | SpriteKind::Chest - | SpriteKind::ChestBuried - | SpriteKind::Mud - | SpriteKind::Crate => Some(SpriteInteractKind::Chest), + // Collectible checked in addition to container for case that sprite requires a tool to + // collect and cannot be collected by hand, yet still meets the container check + _ if sprite_kind.is_container() && sprite_kind.is_collectible() => { + Some(SpriteInteractKind::Chest) + }, _ if sprite_kind.is_collectible() => Some(SpriteInteractKind::Fallback), _ => None, } diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 1fce3b2b8c..b4df9f7d6a 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -7,8 +7,8 @@ use entity_creation::{ }; use entity_manipulation::{ handle_aura, handle_bonk, handle_buff, handle_combo_change, handle_damage, handle_delete, - handle_destroy, handle_energy_change, handle_entity_attacked_hook, handle_explosion, handle_knockback, - handle_land_on_ground, handle_poise, handle_respawn, handle_teleport_to, + handle_destroy, handle_energy_change, handle_entity_attacked_hook, handle_explosion, + handle_knockback, handle_land_on_ground, handle_poise, handle_respawn, handle_teleport_to, }; use group_manip::handle_group; use information::handle_site_info;