mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'newclarityex/petting-animals' into 'master'
newclarityex/petting animals See merge request veloren/veloren!4408
This commit is contained in:
commit
4ee31ab020
@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Petting animals tamed by you or someone else!
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Fireworks and bombs are (again) available from chests (Sahagin and above).
|
- Fireworks and bombs are (again) available from chests (Sahagin and above).
|
||||||
|
@ -54,6 +54,7 @@ hud-mine-needs_pickaxe = Needs Pickaxe
|
|||||||
hud-mine-needs_shovel = Needs Shovel
|
hud-mine-needs_shovel = Needs Shovel
|
||||||
hud-mine-needs_unhandled_case = Needs ???
|
hud-mine-needs_unhandled_case = Needs ???
|
||||||
hud-talk = Talk
|
hud-talk = Talk
|
||||||
|
hud-pet = Pet
|
||||||
hud-trade = Trade
|
hud-trade = Trade
|
||||||
hud-mount = Mount
|
hud-mount = Mount
|
||||||
hud-follow = Follow
|
hud-follow = Follow
|
||||||
|
@ -1419,6 +1419,16 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_pet(&mut self, target_entity: EcsEntity) {
|
||||||
|
if self.is_dead() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(target_uid) = self.state.read_component_copied(target_entity) {
|
||||||
|
self.control_action(ControlAction::Pet { target_uid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn npc_interact(&mut self, npc_entity: EcsEntity, subject: Subject) {
|
pub fn npc_interact(&mut self, npc_entity: EcsEntity, subject: Subject) {
|
||||||
// If we're dead, exit before sending message
|
// If we're dead, exit before sending message
|
||||||
if self.is_dead() {
|
if self.is_dead() {
|
||||||
|
@ -717,6 +717,7 @@ impl From<&CharacterState> for CharacterAbilityType {
|
|||||||
| CharacterState::Climb(_)
|
| CharacterState::Climb(_)
|
||||||
| CharacterState::Sit
|
| CharacterState::Sit
|
||||||
| CharacterState::Dance
|
| CharacterState::Dance
|
||||||
|
| CharacterState::Pet(_)
|
||||||
| CharacterState::Talk
|
| CharacterState::Talk
|
||||||
| CharacterState::Glide(_)
|
| CharacterState::Glide(_)
|
||||||
| CharacterState::GlideWield(_)
|
| CharacterState::GlideWield(_)
|
||||||
|
@ -102,6 +102,7 @@ pub enum CharacterState {
|
|||||||
Sit,
|
Sit,
|
||||||
Dance,
|
Dance,
|
||||||
Talk,
|
Talk,
|
||||||
|
Pet(pet::Data),
|
||||||
Glide(glide::Data),
|
Glide(glide::Data),
|
||||||
GlideWield(glide_wield::Data),
|
GlideWield(glide_wield::Data),
|
||||||
/// A stunned state
|
/// A stunned state
|
||||||
@ -315,6 +316,7 @@ impl CharacterState {
|
|||||||
CharacterState::Climb(_)
|
CharacterState::Climb(_)
|
||||||
| CharacterState::Equipping(_)
|
| CharacterState::Equipping(_)
|
||||||
| CharacterState::Dance
|
| CharacterState::Dance
|
||||||
|
| CharacterState::Pet(_)
|
||||||
| CharacterState::Glide(_)
|
| CharacterState::Glide(_)
|
||||||
| CharacterState::GlideWield(_)
|
| CharacterState::GlideWield(_)
|
||||||
| CharacterState::Talk
|
| CharacterState::Talk
|
||||||
@ -508,6 +510,7 @@ impl CharacterState {
|
|||||||
CharacterState::Stunned(data) => data.behavior(j, output_events),
|
CharacterState::Stunned(data) => data.behavior(j, output_events),
|
||||||
CharacterState::Sit => sit::Data::behavior(&sit::Data, j, output_events),
|
CharacterState::Sit => sit::Data::behavior(&sit::Data, j, output_events),
|
||||||
CharacterState::Dance => dance::Data::behavior(&dance::Data, j, output_events),
|
CharacterState::Dance => dance::Data::behavior(&dance::Data, j, output_events),
|
||||||
|
CharacterState::Pet(data) => data.behavior(j, output_events),
|
||||||
CharacterState::BasicBlock(data) => data.behavior(j, output_events),
|
CharacterState::BasicBlock(data) => data.behavior(j, output_events),
|
||||||
CharacterState::Roll(data) => data.behavior(j, output_events),
|
CharacterState::Roll(data) => data.behavior(j, output_events),
|
||||||
CharacterState::Wielding(data) => data.behavior(j, output_events),
|
CharacterState::Wielding(data) => data.behavior(j, output_events),
|
||||||
@ -562,6 +565,7 @@ impl CharacterState {
|
|||||||
CharacterState::Dance => {
|
CharacterState::Dance => {
|
||||||
states::dance::Data::handle_event(&dance::Data, j, output_events, action)
|
states::dance::Data::handle_event(&dance::Data, j, output_events, action)
|
||||||
},
|
},
|
||||||
|
CharacterState::Pet(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action),
|
CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::Roll(data) => data.handle_event(j, output_events, action),
|
CharacterState::Roll(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::Wielding(data) => data.handle_event(j, output_events, action),
|
CharacterState::Wielding(data) => data.handle_event(j, output_events, action),
|
||||||
@ -618,6 +622,7 @@ impl CharacterState {
|
|||||||
CharacterState::Stunned(_) => None,
|
CharacterState::Stunned(_) => None,
|
||||||
CharacterState::Sit => None,
|
CharacterState::Sit => None,
|
||||||
CharacterState::Dance => None,
|
CharacterState::Dance => None,
|
||||||
|
CharacterState::Pet(_) => None,
|
||||||
CharacterState::BasicBlock(data) => Some(data.static_data.ability_info),
|
CharacterState::BasicBlock(data) => Some(data.static_data.ability_info),
|
||||||
CharacterState::Roll(data) => Some(data.static_data.ability_info),
|
CharacterState::Roll(data) => Some(data.static_data.ability_info),
|
||||||
CharacterState::Wielding(_) => None,
|
CharacterState::Wielding(_) => None,
|
||||||
@ -663,6 +668,7 @@ impl CharacterState {
|
|||||||
CharacterState::Stunned(data) => Some(data.stage_section),
|
CharacterState::Stunned(data) => Some(data.stage_section),
|
||||||
CharacterState::Sit => None,
|
CharacterState::Sit => None,
|
||||||
CharacterState::Dance => None,
|
CharacterState::Dance => None,
|
||||||
|
CharacterState::Pet(_) => None,
|
||||||
CharacterState::BasicBlock(data) => Some(data.stage_section),
|
CharacterState::BasicBlock(data) => Some(data.stage_section),
|
||||||
CharacterState::Roll(data) => Some(data.stage_section),
|
CharacterState::Roll(data) => Some(data.stage_section),
|
||||||
CharacterState::Equipping(_) => Some(StageSection::Buildup),
|
CharacterState::Equipping(_) => Some(StageSection::Buildup),
|
||||||
@ -712,6 +718,7 @@ impl CharacterState {
|
|||||||
}),
|
}),
|
||||||
CharacterState::Sit => None,
|
CharacterState::Sit => None,
|
||||||
CharacterState::Dance => None,
|
CharacterState::Dance => None,
|
||||||
|
CharacterState::Pet(_) => None,
|
||||||
CharacterState::BasicBlock(data) => Some(DurationsInfo {
|
CharacterState::BasicBlock(data) => Some(DurationsInfo {
|
||||||
buildup: Some(data.static_data.buildup_duration),
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
recover: Some(data.static_data.recover_duration),
|
recover: Some(data.static_data.recover_duration),
|
||||||
@ -901,6 +908,7 @@ impl CharacterState {
|
|||||||
CharacterState::Stunned(data) => Some(data.timer),
|
CharacterState::Stunned(data) => Some(data.timer),
|
||||||
CharacterState::Sit => None,
|
CharacterState::Sit => None,
|
||||||
CharacterState::Dance => None,
|
CharacterState::Dance => None,
|
||||||
|
CharacterState::Pet(_) => None,
|
||||||
CharacterState::BasicBlock(data) => Some(data.timer),
|
CharacterState::BasicBlock(data) => Some(data.timer),
|
||||||
CharacterState::Roll(data) => Some(data.timer),
|
CharacterState::Roll(data) => Some(data.timer),
|
||||||
CharacterState::Wielding(_) => None,
|
CharacterState::Wielding(_) => None,
|
||||||
@ -946,6 +954,7 @@ impl CharacterState {
|
|||||||
CharacterState::Stunned(_) => None,
|
CharacterState::Stunned(_) => None,
|
||||||
CharacterState::Sit => None,
|
CharacterState::Sit => None,
|
||||||
CharacterState::Dance => None,
|
CharacterState::Dance => None,
|
||||||
|
CharacterState::Pet(_) => None,
|
||||||
CharacterState::BasicBlock(_) => None,
|
CharacterState::BasicBlock(_) => None,
|
||||||
CharacterState::Roll(_) => None,
|
CharacterState::Roll(_) => None,
|
||||||
CharacterState::Wielding(_) => None,
|
CharacterState::Wielding(_) => None,
|
||||||
|
@ -181,6 +181,9 @@ pub enum ControlAction {
|
|||||||
Unwield,
|
Unwield,
|
||||||
Sit,
|
Sit,
|
||||||
Dance,
|
Dance,
|
||||||
|
Pet {
|
||||||
|
target_uid: Uid,
|
||||||
|
},
|
||||||
Sneak,
|
Sneak,
|
||||||
Stand,
|
Stand,
|
||||||
Talk,
|
Talk,
|
||||||
|
@ -3,18 +3,18 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
item::{tool::AbilityMap, MaterialStatManifest},
|
item::{tool::AbilityMap, MaterialStatManifest},
|
||||||
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, ControlAction,
|
ActiveAbilities, Alignment, Beam, Body, CharacterActivity, CharacterState, Combo,
|
||||||
Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory,
|
ControlAction, Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind,
|
||||||
InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, Scale, SkillSet, Stance, StateUpdate,
|
Inventory, InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, PreviousPhysCache, Scale,
|
||||||
Stats, Vel,
|
SkillSet, Stance, StateUpdate, Stats, Vel,
|
||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Rider, VolumeRider},
|
mounting::{Rider, VolumeRider},
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::Uid,
|
uid::{IdMaps, Uid},
|
||||||
};
|
};
|
||||||
use specs::{storage::FlaggedAccessMut, Entity, LazyUpdate};
|
use specs::{storage::FlaggedAccessMut, Entity, LazyUpdate, Read, ReadStorage};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub trait CharacterBehavior {
|
pub trait CharacterBehavior {
|
||||||
@ -50,6 +50,14 @@ pub trait CharacterBehavior {
|
|||||||
fn dance(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
fn dance(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
StateUpdate::from(data)
|
StateUpdate::from(data)
|
||||||
}
|
}
|
||||||
|
fn pet(
|
||||||
|
&self,
|
||||||
|
data: &JoinData,
|
||||||
|
_output_events: &mut OutputEvents,
|
||||||
|
_target_uid: Uid,
|
||||||
|
) -> StateUpdate {
|
||||||
|
StateUpdate::from(data)
|
||||||
|
}
|
||||||
fn sneak(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
fn sneak(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
StateUpdate::from(data)
|
StateUpdate::from(data)
|
||||||
}
|
}
|
||||||
@ -96,6 +104,7 @@ pub trait CharacterBehavior {
|
|||||||
ControlAction::Unwield => self.unwield(data, output_events),
|
ControlAction::Unwield => self.unwield(data, output_events),
|
||||||
ControlAction::Sit => self.sit(data, output_events),
|
ControlAction::Sit => self.sit(data, output_events),
|
||||||
ControlAction::Dance => self.dance(data, output_events),
|
ControlAction::Dance => self.dance(data, output_events),
|
||||||
|
ControlAction::Pet { target_uid } => self.pet(data, output_events, target_uid),
|
||||||
ControlAction::Sneak => {
|
ControlAction::Sneak => {
|
||||||
if data.mount_data.is_none() && data.volume_mount_data.is_none() {
|
if data.mount_data.is_none() && data.volume_mount_data.is_none() {
|
||||||
self.sneak(data, output_events)
|
self.sneak(data, output_events)
|
||||||
@ -149,6 +158,9 @@ pub struct JoinData<'a> {
|
|||||||
pub mount_data: Option<&'a Is<Rider>>,
|
pub mount_data: Option<&'a Is<Rider>>,
|
||||||
pub volume_mount_data: Option<&'a Is<VolumeRider>>,
|
pub volume_mount_data: Option<&'a Is<VolumeRider>>,
|
||||||
pub stance: Option<&'a Stance>,
|
pub stance: Option<&'a Stance>,
|
||||||
|
pub id_maps: &'a Read<'a, IdMaps>,
|
||||||
|
pub alignments: &'a ReadStorage<'a, Alignment>,
|
||||||
|
pub prev_phys_caches: &'a ReadStorage<'a, PreviousPhysCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct JoinStruct<'a> {
|
pub struct JoinStruct<'a> {
|
||||||
@ -179,6 +191,9 @@ pub struct JoinStruct<'a> {
|
|||||||
pub mount_data: Option<&'a Is<Rider>>,
|
pub mount_data: Option<&'a Is<Rider>>,
|
||||||
pub volume_mount_data: Option<&'a Is<VolumeRider>>,
|
pub volume_mount_data: Option<&'a Is<VolumeRider>>,
|
||||||
pub stance: Option<&'a Stance>,
|
pub stance: Option<&'a Stance>,
|
||||||
|
pub id_maps: &'a Read<'a, IdMaps>,
|
||||||
|
pub alignments: &'a ReadStorage<'a, Alignment>,
|
||||||
|
pub prev_phys_caches: &'a ReadStorage<'a, PreviousPhysCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> JoinData<'a> {
|
impl<'a> JoinData<'a> {
|
||||||
@ -223,6 +238,9 @@ impl<'a> JoinData<'a> {
|
|||||||
mount_data: j.mount_data,
|
mount_data: j.mount_data,
|
||||||
volume_mount_data: j.volume_mount_data,
|
volume_mount_data: j.volume_mount_data,
|
||||||
stance: j.stance,
|
stance: j.stance,
|
||||||
|
id_maps: j.id_maps,
|
||||||
|
alignments: j.alignments,
|
||||||
|
prev_phys_caches: j.prev_phys_caches,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
idle,
|
idle,
|
||||||
},
|
},
|
||||||
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -50,6 +51,12 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pet(&self, data: &JoinData, _: &mut OutputEvents, target_uid: Uid) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_pet(data, &mut update, target_uid);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
resources::Time,
|
resources::Time,
|
||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -96,6 +97,12 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pet(&self, data: &JoinData, _: &mut OutputEvents, target_uid: Uid) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_pet(data, &mut update, target_uid);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(Data {
|
update.character = CharacterState::Idle(Data {
|
||||||
|
@ -23,6 +23,7 @@ pub mod idle;
|
|||||||
pub mod leap_melee;
|
pub mod leap_melee;
|
||||||
pub mod leap_shockwave;
|
pub mod leap_shockwave;
|
||||||
pub mod music;
|
pub mod music;
|
||||||
|
pub mod pet;
|
||||||
pub mod rapid_melee;
|
pub mod rapid_melee;
|
||||||
pub mod repeater_ranged;
|
pub mod repeater_ranged;
|
||||||
pub mod riposte_melee;
|
pub mod riposte_melee;
|
||||||
|
99
common/src/states/pet.rs
Normal file
99
common/src/states/pet.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
use super::utils::*;
|
||||||
|
use crate::{
|
||||||
|
comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate},
|
||||||
|
states::{
|
||||||
|
behavior::{CharacterBehavior, JoinData},
|
||||||
|
idle,
|
||||||
|
},
|
||||||
|
uid::Uid,
|
||||||
|
util::Dir,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct StaticData {
|
||||||
|
pub target_uid: Uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Data {
|
||||||
|
pub static_data: StaticData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterBehavior for Data {
|
||||||
|
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
|
||||||
|
let target_entity = data.id_maps.uid_entity(self.static_data.target_uid);
|
||||||
|
let target_pos = target_entity
|
||||||
|
.and_then(|target_entity| data.prev_phys_caches.get(target_entity))
|
||||||
|
.and_then(|prev_phys| prev_phys.pos);
|
||||||
|
|
||||||
|
let can_pet = target_entity.map_or(false, |target_entity| {
|
||||||
|
target_pos.zip(data.alignments.get(target_entity)).map_or(
|
||||||
|
false,
|
||||||
|
|(target_position, target_alignment)| {
|
||||||
|
can_perform_pet(*data.pos, target_position, *target_alignment)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Face target if they have a position.
|
||||||
|
if let Some(target_pos) = target_pos {
|
||||||
|
let ori_dir = Dir::from_unnormalized(Vec3::from((target_pos.0 - data.pos.0).xy()));
|
||||||
|
handle_orientation(data, &mut update, 1.0, ori_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
leave_stance(data, output_events);
|
||||||
|
handle_wield(data, &mut update);
|
||||||
|
handle_jump(data, output_events, &mut update, 1.0);
|
||||||
|
|
||||||
|
if !can_pet {
|
||||||
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to Fall/Stand up/Move
|
||||||
|
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||||
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manipulate_loadout(
|
||||||
|
&self,
|
||||||
|
data: &JoinData,
|
||||||
|
output_events: &mut OutputEvents,
|
||||||
|
inv_action: InventoryAction,
|
||||||
|
) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
handle_manipulate_loadout(data, output_events, &mut update, inv_action);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_wield(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_dance(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_sit(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
// Try to Fall/Stand up/Move
|
||||||
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
|
update
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
idle,
|
idle,
|
||||||
},
|
},
|
||||||
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -54,6 +55,12 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pet(&self, data: &JoinData, _: &mut OutputEvents, target_uid: Uid) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_pet(data, &mut update, target_uid);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
|
@ -15,15 +15,16 @@ use crate::{
|
|||||||
},
|
},
|
||||||
quadruped_low, quadruped_medium, quadruped_small, ship,
|
quadruped_low, quadruped_medium, quadruped_small, ship,
|
||||||
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
|
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
|
||||||
theropod, Body, CharacterState, Density, InputAttr, InputKind, InventoryAction, Melee,
|
theropod, Alignment, Body, CharacterState, Density, InputAttr, InputKind, InventoryAction,
|
||||||
StateUpdate,
|
Melee, Pos, StateUpdate,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
consts::{FRIC_GROUND, GRAVITY, MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
|
||||||
event::{BuffEvent, ChangeStanceEvent, ComboChangeEvent, InventoryManipEvent, LocalEvent},
|
event::{BuffEvent, ChangeStanceEvent, ComboChangeEvent, InventoryManipEvent, LocalEvent},
|
||||||
mounting::Volume,
|
mounting::Volume,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||||
terrain::{Block, TerrainGrid, UnlockKind},
|
terrain::{Block, TerrainGrid, UnlockKind},
|
||||||
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
@ -883,6 +884,34 @@ pub fn attempt_dance(data: &JoinData<'_>, update: &mut StateUpdate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn can_perform_pet(position: Pos, target_position: Pos, target_alignment: Alignment) -> bool {
|
||||||
|
let within_distance = position.0.distance_squared(target_position.0) <= MAX_MOUNT_RANGE.powi(2);
|
||||||
|
let valid_alignment = matches!(target_alignment, Alignment::Owned(_) | Alignment::Tame);
|
||||||
|
|
||||||
|
within_distance && valid_alignment
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attempt_pet(data: &JoinData<'_>, update: &mut StateUpdate, target_uid: Uid) {
|
||||||
|
let can_pet = data
|
||||||
|
.id_maps
|
||||||
|
.uid_entity(target_uid)
|
||||||
|
.and_then(|target_entity| {
|
||||||
|
data.prev_phys_caches
|
||||||
|
.get(target_entity)
|
||||||
|
.and_then(|prev_phys| prev_phys.pos)
|
||||||
|
.zip(data.alignments.get(target_entity))
|
||||||
|
})
|
||||||
|
.map_or(false, |(target_position, target_alignment)| {
|
||||||
|
can_perform_pet(*data.pos, target_position, *target_alignment)
|
||||||
|
});
|
||||||
|
|
||||||
|
if can_pet && data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||||
|
update.character = CharacterState::Pet(pet::Data {
|
||||||
|
static_data: pet::StaticData { target_uid },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn attempt_talk(data: &JoinData<'_>, update: &mut StateUpdate) {
|
pub fn attempt_talk(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||||
if data.physics.on_ground.is_some() {
|
if data.physics.on_ground.is_some() {
|
||||||
update.character = CharacterState::Talk;
|
update.character = CharacterState::Talk;
|
||||||
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
idle,
|
idle,
|
||||||
},
|
},
|
||||||
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -104,6 +105,12 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pet(&self, data: &JoinData, _: &mut OutputEvents, target_uid: Uid) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_pet(data, &mut update, target_uid);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||||
|
@ -9,7 +9,7 @@ use common::{
|
|||||||
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
||||||
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density,
|
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density,
|
||||||
Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos,
|
Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos,
|
||||||
Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
|
PreviousPhysCache, Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{self, EventBus, KnockbackEvent, LocalEvent},
|
event::{self, EventBus, KnockbackEvent, LocalEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
@ -21,7 +21,7 @@ use common::{
|
|||||||
idle,
|
idle,
|
||||||
},
|
},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::Uid,
|
uid::{IdMaps, Uid},
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
|
|
||||||
@ -53,6 +53,7 @@ pub struct ReadData<'a> {
|
|||||||
terrain: ReadExpect<'a, TerrainGrid>,
|
terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
inventories: ReadStorage<'a, Inventory>,
|
inventories: ReadStorage<'a, Inventory>,
|
||||||
stances: ReadStorage<'a, Stance>,
|
stances: ReadStorage<'a, Stance>,
|
||||||
|
prev_phys_caches: ReadStorage<'a, PreviousPhysCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ## Character Behavior System
|
/// ## Character Behavior System
|
||||||
@ -74,6 +75,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
WriteStorage<'a, Poise>,
|
WriteStorage<'a, Poise>,
|
||||||
Read<'a, EventBus<Outcome>>,
|
Read<'a, EventBus<Outcome>>,
|
||||||
|
Read<'a, IdMaps>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "character_behavior";
|
const NAME: &'static str = "character_behavior";
|
||||||
@ -94,6 +96,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut controllers,
|
mut controllers,
|
||||||
mut poises,
|
mut poises,
|
||||||
outcomes,
|
outcomes,
|
||||||
|
id_maps,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut local_emitter = read_data.local_bus.emitter();
|
let mut local_emitter = read_data.local_bus.emitter();
|
||||||
@ -217,6 +220,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
mount_data: read_data.is_riders.get(entity),
|
mount_data: read_data.is_riders.get(entity),
|
||||||
volume_mount_data: read_data.is_volume_riders.get(entity),
|
volume_mount_data: read_data.is_volume_riders.get(entity),
|
||||||
stance: read_data.stances.get(entity),
|
stance: read_data.stances.get(entity),
|
||||||
|
id_maps: &id_maps,
|
||||||
|
alignments: &read_data.alignments,
|
||||||
|
prev_phys_caches: &read_data.prev_phys_caches,
|
||||||
};
|
};
|
||||||
|
|
||||||
for action in actions {
|
for action in actions {
|
||||||
|
@ -131,6 +131,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
CharacterState::Idle(_)
|
CharacterState::Idle(_)
|
||||||
| CharacterState::Talk
|
| CharacterState::Talk
|
||||||
| CharacterState::Dance
|
| CharacterState::Dance
|
||||||
|
| CharacterState::Pet(_)
|
||||||
| CharacterState::Skate(_)
|
| CharacterState::Skate(_)
|
||||||
| CharacterState::Glide(_)
|
| CharacterState::Glide(_)
|
||||||
| CharacterState::GlideWield(_)
|
| CharacterState::GlideWield(_)
|
||||||
|
@ -445,6 +445,28 @@ impl<'a> AgentData<'a> {
|
|||||||
None => {},
|
None => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let owner_uid = self.alignment.and_then(|alignment| match alignment {
|
||||||
|
Alignment::Owned(owner_uid) => Some(owner_uid),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let owner = owner_uid.and_then(|owner_uid| get_entity_by_id(*owner_uid, read_data));
|
||||||
|
|
||||||
|
let is_being_pet = owner
|
||||||
|
.and_then(|owner| read_data.char_states.get(owner))
|
||||||
|
.map_or(false, |char_state| match char_state {
|
||||||
|
CharacterState::Pet(petting_data) => {
|
||||||
|
petting_data.static_data.target_uid == *self.uid
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let is_in_range = owner
|
||||||
|
.and_then(|owner| read_data.positions.get(owner))
|
||||||
|
.map_or(false, |pos| {
|
||||||
|
pos.0.distance_squared(self.pos.0) < MAX_MOUNT_RANGE.powi(2)
|
||||||
|
});
|
||||||
|
|
||||||
// Idle NPCs should try to jump on the shoulders of their owner, sometimes.
|
// Idle NPCs should try to jump on the shoulders of their owner, sometimes.
|
||||||
if read_data.is_riders.contains(*self.entity) {
|
if read_data.is_riders.contains(*self.entity) {
|
||||||
if rng.gen_bool(0.0001) {
|
if rng.gen_bool(0.0001) {
|
||||||
@ -452,10 +474,9 @@ impl<'a> AgentData<'a> {
|
|||||||
} else {
|
} else {
|
||||||
break 'activity;
|
break 'activity;
|
||||||
}
|
}
|
||||||
} else if let Some(Alignment::Owned(owner_uid)) = self.alignment
|
} else if let Some(owner_uid) = owner_uid
|
||||||
&& let Some(owner) = get_entity_by_id(*owner_uid, read_data)
|
&& is_in_range
|
||||||
&& let Some(pos) = read_data.positions.get(owner)
|
&& !is_being_pet
|
||||||
&& pos.0.distance_squared(self.pos.0) < MAX_MOUNT_RANGE.powi(2)
|
|
||||||
&& rng.gen_bool(0.01)
|
&& rng.gen_bool(0.01)
|
||||||
{
|
{
|
||||||
controller.push_event(ControlEvent::Mount(*owner_uid));
|
controller.push_event(ControlEvent::Mount(*owner_uid));
|
||||||
|
@ -20,6 +20,7 @@ pub mod jump;
|
|||||||
pub mod leapmelee;
|
pub mod leapmelee;
|
||||||
pub mod mount;
|
pub mod mount;
|
||||||
pub mod music;
|
pub mod music;
|
||||||
|
pub mod pet;
|
||||||
pub mod rapidmelee;
|
pub mod rapidmelee;
|
||||||
pub mod repeater;
|
pub mod repeater;
|
||||||
pub mod ripostemelee;
|
pub mod ripostemelee;
|
||||||
@ -51,10 +52,11 @@ pub use self::{
|
|||||||
dance::DanceAnimation, dash::DashAnimation, divemelee::DiveMeleeAnimation,
|
dance::DanceAnimation, dash::DashAnimation, divemelee::DiveMeleeAnimation,
|
||||||
equip::EquipAnimation, finishermelee::FinisherMeleeAnimation, glidewield::GlideWieldAnimation,
|
equip::EquipAnimation, finishermelee::FinisherMeleeAnimation, glidewield::GlideWieldAnimation,
|
||||||
gliding::GlidingAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation,
|
gliding::GlidingAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation,
|
||||||
mount::MountAnimation, music::MusicAnimation, rapidmelee::RapidMeleeAnimation,
|
mount::MountAnimation, music::MusicAnimation, pet::PetAnimation,
|
||||||
repeater::RepeaterAnimation, ripostemelee::RiposteMeleeAnimation, roll::RollAnimation,
|
rapidmelee::RapidMeleeAnimation, repeater::RepeaterAnimation,
|
||||||
run::RunAnimation, selfbuff::SelfBuffAnimation, shockwave::ShockwaveAnimation,
|
ripostemelee::RiposteMeleeAnimation, roll::RollAnimation, run::RunAnimation,
|
||||||
shoot::ShootAnimation, sit::SitAnimation, sleep::SleepAnimation, sneak::SneakAnimation,
|
selfbuff::SelfBuffAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation,
|
||||||
|
sit::SitAnimation, sleep::SleepAnimation, sneak::SneakAnimation,
|
||||||
sneakequip::SneakEquipAnimation, sneakwield::SneakWieldAnimation,
|
sneakequip::SneakEquipAnimation, sneakwield::SneakWieldAnimation,
|
||||||
staggered::StaggeredAnimation, stand::StandAnimation, steer::SteerAnimation,
|
staggered::StaggeredAnimation, stand::StandAnimation, steer::SteerAnimation,
|
||||||
stunned::StunnedAnimation, swim::SwimAnimation, swimwield::SwimWieldAnimation,
|
stunned::StunnedAnimation, swim::SwimAnimation, swimwield::SwimWieldAnimation,
|
||||||
|
48
voxygen/anim/src/character/pet.rs
Normal file
48
voxygen/anim/src/character/pet.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use super::{
|
||||||
|
super::{vek::*, Animation},
|
||||||
|
CharacterSkeleton, SkeletonAttr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
pub struct PetAnimation;
|
||||||
|
|
||||||
|
impl Animation for PetAnimation {
|
||||||
|
type Dependency<'a> = (Vec3<f32>, Option<vek::Vec3<f32>>, f32);
|
||||||
|
type Skeleton = CharacterSkeleton;
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
const UPDATE_FN: &'static [u8] = b"character_pet\0";
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_pet")]
|
||||||
|
fn update_skeleton_inner(
|
||||||
|
skeleton: &Self::Skeleton,
|
||||||
|
(pos, target_pos, _global_time): Self::Dependency<'_>,
|
||||||
|
anim_time: f32,
|
||||||
|
_rate: &mut f32,
|
||||||
|
s_a: &SkeletonAttr,
|
||||||
|
) -> Self::Skeleton {
|
||||||
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
|
let fast = (anim_time * 3.0).sin();
|
||||||
|
let fast_offset = (anim_time * 3.0 + PI * 0.5).sin();
|
||||||
|
|
||||||
|
let z_diff = target_pos.map_or(0., |target_pos| target_pos.z - pos.z);
|
||||||
|
|
||||||
|
// Tilt head down by 10 deg
|
||||||
|
next.head.orientation = Quaternion::rotation_x(-1. * PI / 2. / 9.);
|
||||||
|
|
||||||
|
// Lift hand up and out, slight hand position change depending on height
|
||||||
|
next.hand_r.position = Vec3::new(
|
||||||
|
s_a.hand.0 + -2. * fast_offset,
|
||||||
|
s_a.hand.1 + 8.0,
|
||||||
|
s_a.hand.2 + 4.0 + 1. * fast + z_diff,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Raise arm 90deg then up and down
|
||||||
|
next.hand_r.orientation =
|
||||||
|
Quaternion::rotation_x(PI / 2. + fast * 0.15).rotated_z(fast_offset * 0.5);
|
||||||
|
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
@ -2465,39 +2465,49 @@ impl Hud {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
Some(comp::Alignment::Owned(owner))
|
Some(comp::Alignment::Owned(owner))
|
||||||
if Some(*owner) == client.uid()
|
if dist_sqr < common::consts::MAX_MOUNT_RANGE.powi(2) =>
|
||||||
&& dist_sqr < common::consts::MAX_MOUNT_RANGE.powi(2) =>
|
|
||||||
{
|
{
|
||||||
let mut options = Vec::new();
|
let mut options = Vec::new();
|
||||||
if is_mount.is_none() {
|
if is_mount.is_none() {
|
||||||
options.push((
|
if Some(*owner) == client.uid() {
|
||||||
GameInput::Trade,
|
|
||||||
i18n.get_msg("hud-trade").to_string(),
|
|
||||||
));
|
|
||||||
if !client.is_riding()
|
|
||||||
&& is_mountable(body, bodies.get(client.entity()))
|
|
||||||
{
|
|
||||||
options.push((
|
options.push((
|
||||||
GameInput::Mount,
|
GameInput::Trade,
|
||||||
i18n.get_msg("hud-mount").to_string(),
|
i18n.get_msg("hud-trade").to_string(),
|
||||||
|
));
|
||||||
|
if !client.is_riding()
|
||||||
|
&& is_mountable(body, bodies.get(client.entity()))
|
||||||
|
{
|
||||||
|
options.push((
|
||||||
|
GameInput::Mount,
|
||||||
|
i18n.get_msg("hud-mount").to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_staying = character_activity
|
||||||
|
.map_or(false, |activity| activity.is_pet_staying);
|
||||||
|
|
||||||
|
options.push((
|
||||||
|
GameInput::StayFollow,
|
||||||
|
i18n.get_msg(if is_staying {
|
||||||
|
"hud-follow"
|
||||||
|
} else {
|
||||||
|
"hud-stay"
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_staying = character_activity
|
// Anyone can pet a tamed animal
|
||||||
.map_or(false, |activity| activity.is_pet_staying);
|
|
||||||
|
|
||||||
options.push((
|
options.push((
|
||||||
GameInput::StayFollow,
|
GameInput::Interact,
|
||||||
i18n.get_msg(if is_staying {
|
i18n.get_msg("hud-pet").to_string(),
|
||||||
"hud-follow"
|
|
||||||
} else {
|
|
||||||
"hud-stay"
|
|
||||||
})
|
|
||||||
.to_string(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
options
|
options
|
||||||
},
|
},
|
||||||
|
Some(comp::Alignment::Tame) => {
|
||||||
|
vec![(GameInput::Interact, i18n.get_msg("hud-pet").to_string())]
|
||||||
|
},
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
},
|
},
|
||||||
&time,
|
&time,
|
||||||
|
@ -2112,6 +2112,22 @@ impl FigureMgr {
|
|||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
CharacterState::Pet(s) => {
|
||||||
|
let target_entity = id_maps.uid_entity(s.static_data.target_uid);
|
||||||
|
let target_pos = target_entity.and_then(|target_entity| {
|
||||||
|
ecs.read_component::<Pos>()
|
||||||
|
.get(target_entity)
|
||||||
|
.map(|pos| pos.0)
|
||||||
|
});
|
||||||
|
|
||||||
|
anim::character::PetAnimation::update_skeleton(
|
||||||
|
&target_base,
|
||||||
|
(pos.0, target_pos, time),
|
||||||
|
state.state_time,
|
||||||
|
&mut state_animation_rate,
|
||||||
|
skeleton_attr,
|
||||||
|
)
|
||||||
|
},
|
||||||
CharacterState::Music(s) => {
|
CharacterState::Music(s) => {
|
||||||
anim::character::MusicAnimation::update_skeleton(
|
anim::character::MusicAnimation::update_skeleton(
|
||||||
&target_base,
|
&target_base,
|
||||||
|
@ -28,6 +28,7 @@ use common::{
|
|||||||
mounting::{Mount, VolumePos},
|
mounting::{Mount, VolumePos},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe,
|
recipe,
|
||||||
|
states::utils::can_perform_pet,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
trade::TradeResult,
|
trade::TradeResult,
|
||||||
util::{Dir, Plane},
|
util::{Dir, Plane},
|
||||||
@ -1074,6 +1075,22 @@ impl PlayState for SessionState {
|
|||||||
.state()
|
.state()
|
||||||
.read_component_cloned::<comp::Body>(*entity);
|
.read_component_cloned::<comp::Body>(*entity);
|
||||||
|
|
||||||
|
let pettable = client
|
||||||
|
.state()
|
||||||
|
.read_component_cloned::<comp::Pos>(*entity)
|
||||||
|
.zip(client.state().read_component_cloned::<comp::Alignment>(*entity))
|
||||||
|
.zip(client.state().read_component_cloned::<comp::Pos>(client.entity()))
|
||||||
|
.map_or(
|
||||||
|
false,
|
||||||
|
|((target_position, target_alignment), client_position)| {
|
||||||
|
can_perform_pet(
|
||||||
|
client_position,
|
||||||
|
target_position,
|
||||||
|
target_alignment,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if client
|
if client
|
||||||
.state()
|
.state()
|
||||||
.ecs()
|
.ecs()
|
||||||
@ -1097,6 +1114,8 @@ impl PlayState for SessionState {
|
|||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
client.activate_portal(portal_uid);
|
client.activate_portal(portal_uid);
|
||||||
|
} else if pettable {
|
||||||
|
client.do_pet(*entity);
|
||||||
} else {
|
} else {
|
||||||
client.npc_interact(*entity, Subject::Regular);
|
client.npc_interact(*entity, Subject::Regular);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user