mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'TelepathicWalrus/stay_follow_pets' into 'master'
Add stay/follow option for pets See merge request veloren/veloren!3906
This commit is contained in:
commit
38c986fa59
@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Recipe for shovel, which is used to dig in mud and graves
|
- Recipe for shovel, which is used to dig in mud and graves
|
||||||
- Recipe for a new leather pack
|
- Recipe for a new leather pack
|
||||||
- Keybinds for zooming the camera (Defaults: ']' for zooming in and '[' for zooming out)
|
- Keybinds for zooming the camera (Defaults: ']' for zooming in and '[' for zooming out)
|
||||||
|
- Added the ability to make pets sit, they wont follow nor defend you in this state
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ gameinput-climbdown = Climb Down
|
|||||||
gameinput-wallleap = Wall Leap
|
gameinput-wallleap = Wall Leap
|
||||||
gameinput-togglelantern = Toggle Lantern
|
gameinput-togglelantern = Toggle Lantern
|
||||||
gameinput-mount = Mount
|
gameinput-mount = Mount
|
||||||
|
gameinput-stay = Stay/Follow
|
||||||
gameinput-chat = Chat
|
gameinput-chat = Chat
|
||||||
gameinput-command = Command
|
gameinput-command = Command
|
||||||
gameinput-escape = Escape
|
gameinput-escape = Escape
|
||||||
|
@ -54,5 +54,7 @@ hud-mine-needs_unhandled_case = Needs ???
|
|||||||
hud-talk = Talk
|
hud-talk = Talk
|
||||||
hud-trade = Trade
|
hud-trade = Trade
|
||||||
hud-mount = Mount
|
hud-mount = Mount
|
||||||
|
hud-follow = Follow
|
||||||
|
hud-stay= Stay
|
||||||
hud-sit = Sit
|
hud-sit = Sit
|
||||||
hud-steer = Steer
|
hud-steer = Steer
|
||||||
|
@ -1477,6 +1477,14 @@ impl Client {
|
|||||||
|
|
||||||
pub fn unmount(&mut self) { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Unmount)); }
|
pub fn unmount(&mut self) { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Unmount)); }
|
||||||
|
|
||||||
|
pub fn set_pet_stay(&mut self, entity: EcsEntity, stay: bool) {
|
||||||
|
if let Some(uid) = self.state.read_component_copied(entity) {
|
||||||
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::SetPetStay(
|
||||||
|
uid, stay,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn respawn(&mut self) {
|
pub fn respawn(&mut self) {
|
||||||
if self
|
if self
|
||||||
.state
|
.state
|
||||||
|
3
common/net/src/synced_components.rs
Normal file → Executable file
3
common/net/src/synced_components.rs
Normal file → Executable file
@ -75,7 +75,6 @@ macro_rules! reexport_comps {
|
|||||||
pub use common::comp::*;
|
pub use common::comp::*;
|
||||||
use common::link::Is;
|
use common::link::Is;
|
||||||
use common::mounting::{Mount, Rider, VolumeRider};
|
use common::mounting::{Mount, Rider, VolumeRider};
|
||||||
|
|
||||||
// We alias these because the identifier used for the
|
// We alias these because the identifier used for the
|
||||||
// component's type is reused as an enum variant name
|
// component's type is reused as an enum variant name
|
||||||
// in the macro's that we pass to `synced_components!`.
|
// in the macro's that we pass to `synced_components!`.
|
||||||
@ -105,8 +104,6 @@ synced_components!(reexport_comps);
|
|||||||
|
|
||||||
use crate::sync::{NetSync, SyncFrom};
|
use crate::sync::{NetSync, SyncFrom};
|
||||||
|
|
||||||
// These are synced from any entity within range.
|
|
||||||
|
|
||||||
impl NetSync for Body {
|
impl NetSync for Body {
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
@ -595,6 +595,7 @@ pub struct Agent {
|
|||||||
/// required and reset each time the flee timer is reset.
|
/// required and reset each time the flee timer is reset.
|
||||||
pub flee_from_pos: Option<Pos>,
|
pub flee_from_pos: Option<Pos>,
|
||||||
pub awareness: Awareness,
|
pub awareness: Awareness,
|
||||||
|
pub stay_pos: Option<Pos>,
|
||||||
/// Inputs sent up to rtsim
|
/// Inputs sent up to rtsim
|
||||||
pub rtsim_outbox: Option<VecDeque<NpcInput>>,
|
pub rtsim_outbox: Option<VecDeque<NpcInput>>,
|
||||||
}
|
}
|
||||||
@ -700,6 +701,7 @@ impl Agent {
|
|||||||
sounds_heard: Vec::new(),
|
sounds_heard: Vec::new(),
|
||||||
position_pid_controller: None,
|
position_pid_controller: None,
|
||||||
flee_from_pos: None,
|
flee_from_pos: None,
|
||||||
|
stay_pos: None,
|
||||||
awareness: Awareness::new(0.0),
|
awareness: Awareness::new(0.0),
|
||||||
rtsim_outbox: None,
|
rtsim_outbox: None,
|
||||||
}
|
}
|
||||||
|
@ -1004,6 +1004,9 @@ pub struct CharacterActivity {
|
|||||||
/// `None` means that the look direction should be derived from the
|
/// `None` means that the look direction should be derived from the
|
||||||
/// orientation
|
/// orientation
|
||||||
pub look_dir: Option<Dir>,
|
pub look_dir: Option<Dir>,
|
||||||
|
/// If true, the owner has set this pet to stay at a fixed location and
|
||||||
|
/// to not engage in combat
|
||||||
|
pub is_pet_staying: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for CharacterActivity {
|
impl Component for CharacterActivity {
|
||||||
|
@ -146,6 +146,7 @@ pub enum ControlEvent {
|
|||||||
Mount(Uid),
|
Mount(Uid),
|
||||||
MountVolume(VolumePos),
|
MountVolume(VolumePos),
|
||||||
Unmount,
|
Unmount,
|
||||||
|
SetPetStay(Uid, bool),
|
||||||
InventoryEvent(InventoryEvent),
|
InventoryEvent(InventoryEvent),
|
||||||
GroupManip(GroupManip),
|
GroupManip(GroupManip),
|
||||||
RemoveBuff(BuffKind),
|
RemoveBuff(BuffKind),
|
||||||
|
0
common/src/comp/pet.rs
Normal file → Executable file
0
common/src/comp/pet.rs
Normal file → Executable file
@ -200,6 +200,7 @@ pub enum ServerEvent {
|
|||||||
Mount(EcsEntity, EcsEntity),
|
Mount(EcsEntity, EcsEntity),
|
||||||
MountVolume(EcsEntity, VolumePos),
|
MountVolume(EcsEntity, VolumePos),
|
||||||
Unmount(EcsEntity),
|
Unmount(EcsEntity),
|
||||||
|
SetPetStay(EcsEntity, EcsEntity, bool),
|
||||||
Possess(Uid, Uid),
|
Possess(Uid, Uid),
|
||||||
/// Inserts default components for a character when loading into the game
|
/// Inserts default components for a character when loading into the game
|
||||||
InitCharacterData {
|
InitCharacterData {
|
||||||
|
@ -63,6 +63,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ControlEvent::SetPetStay(pet_uid, stay) => {
|
||||||
|
if let Some(pet_entity) = read_data.id_maps.uid_entity(pet_uid) {
|
||||||
|
server_emitter.emit(ServerEvent::SetPetStay(entity, pet_entity, stay));
|
||||||
|
}
|
||||||
|
},
|
||||||
ControlEvent::RemoveBuff(buff_id) => {
|
ControlEvent::RemoveBuff(buff_id) => {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
server_emitter.emit(ServerEvent::Buff {
|
||||||
entity,
|
entity,
|
||||||
|
@ -3,6 +3,9 @@ pub const FLEE_DURATION: f32 = 3.0;
|
|||||||
pub const NPC_PICKUP_RANGE: f32 = 2.5;
|
pub const NPC_PICKUP_RANGE: f32 = 2.5;
|
||||||
pub const MAX_PATROL_DIST: f32 = 50.0;
|
pub const MAX_PATROL_DIST: f32 = 50.0;
|
||||||
pub const MAX_PATH_DIST: f32 = 170.0;
|
pub const MAX_PATH_DIST: f32 = 170.0;
|
||||||
|
/// If the pet is any further than this value from its stay position, it will
|
||||||
|
/// start walking back there
|
||||||
|
pub const MAX_STAY_DISTANCE: f32 = 10.0;
|
||||||
pub const PARTIAL_PATH_DIST: f32 = 50.0;
|
pub const PARTIAL_PATH_DIST: f32 = 50.0;
|
||||||
pub const SEPARATION_DIST: f32 = 10.0;
|
pub const SEPARATION_DIST: f32 = 10.0;
|
||||||
pub const SEPARATION_BIAS: f32 = 0.8;
|
pub const SEPARATION_BIAS: f32 = 0.8;
|
||||||
|
0
server/agent/src/data.rs
Normal file → Executable file
0
server/agent/src/data.rs
Normal file → Executable file
52
server/src/events/interaction.rs
Normal file → Executable file
52
server/src/events/interaction.rs
Normal file → Executable file
@ -17,7 +17,7 @@ use common::{
|
|||||||
consts::{MAX_MOUNT_RANGE, MAX_SPRITE_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
consts::{MAX_MOUNT_RANGE, MAX_SPRITE_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
mounting::{Mount, Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
rtsim::RtSimVehicle,
|
rtsim::RtSimVehicle,
|
||||||
terrain::{Block, SpriteKind},
|
terrain::{Block, SpriteKind},
|
||||||
@ -132,7 +132,15 @@ pub fn handle_mount(server: &mut Server, rider: EcsEntity, mount: EcsEntity) {
|
|||||||
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
||||||
});
|
});
|
||||||
|
|
||||||
if (is_pet_of(mount, rider_uid) || is_pet_of(rider, mount_uid)) && can_ride {
|
let is_stay = state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::Agent>()
|
||||||
|
.get(mount)
|
||||||
|
.and_then(|x| x.stay_pos)
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if (is_pet_of(mount, rider_uid) || is_pet_of(rider, mount_uid)) && can_ride && !is_stay
|
||||||
|
{
|
||||||
drop(uids);
|
drop(uids);
|
||||||
let _ = state.link(Mounting {
|
let _ = state.link(Mounting {
|
||||||
mount: mount_uid,
|
mount: mount_uid,
|
||||||
@ -202,6 +210,46 @@ pub fn handle_unmount(server: &mut Server, rider: EcsEntity) {
|
|||||||
state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
|
state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_set_pet_stay(
|
||||||
|
server: &mut Server,
|
||||||
|
command_giver: EcsEntity,
|
||||||
|
pet: EcsEntity,
|
||||||
|
stay: bool,
|
||||||
|
) {
|
||||||
|
let state = server.state_mut();
|
||||||
|
let positions = state.ecs().read_storage::<Pos>();
|
||||||
|
let is_owner = state
|
||||||
|
.ecs()
|
||||||
|
.uid_from_entity(command_giver)
|
||||||
|
.map_or(false, |owner_uid| {
|
||||||
|
matches!(
|
||||||
|
state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::Alignment>()
|
||||||
|
.get(pet),
|
||||||
|
Some(comp::Alignment::Owned(pet_owner)) if *pet_owner == owner_uid,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let current_pet_position = positions.get(pet).copied();
|
||||||
|
let stay = stay && current_pet_position.is_some();
|
||||||
|
if is_owner
|
||||||
|
&& within_mounting_range(positions.get(command_giver), positions.get(pet))
|
||||||
|
&& state.ecs().read_storage::<Is<Mount>>().get(pet).is_none()
|
||||||
|
{
|
||||||
|
state
|
||||||
|
.ecs()
|
||||||
|
.write_storage::<comp::CharacterActivity>()
|
||||||
|
.get_mut(pet)
|
||||||
|
.map(|mut activity| activity.is_pet_staying = stay);
|
||||||
|
state
|
||||||
|
.ecs()
|
||||||
|
.write_storage::<comp::Agent>()
|
||||||
|
.get_mut(pet)
|
||||||
|
.map(|s| s.stay_pos = current_pet_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<&Pos>) -> bool {
|
fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<&Pos>) -> bool {
|
||||||
match (player_position, mount_position) {
|
match (player_position, mount_position) {
|
||||||
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_MOUNT_RANGE.powi(2),
|
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_MOUNT_RANGE.powi(2),
|
||||||
|
@ -23,7 +23,7 @@ use group_manip::handle_group;
|
|||||||
use information::handle_site_info;
|
use information::handle_site_info;
|
||||||
use interaction::{
|
use interaction::{
|
||||||
handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction,
|
handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction,
|
||||||
handle_sound, handle_unmount,
|
handle_set_pet_stay, handle_sound, handle_unmount,
|
||||||
};
|
};
|
||||||
use inventory_manip::handle_inventory;
|
use inventory_manip::handle_inventory;
|
||||||
use invite::{handle_invite, handle_invite_response};
|
use invite::{handle_invite, handle_invite_response};
|
||||||
@ -144,6 +144,9 @@ impl Server {
|
|||||||
handle_mount_volume(self, mounter, volume)
|
handle_mount_volume(self, mounter, volume)
|
||||||
},
|
},
|
||||||
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
||||||
|
ServerEvent::SetPetStay(command_giver, pet, stay) => {
|
||||||
|
handle_set_pet_stay(self, command_giver, pet, stay)
|
||||||
|
},
|
||||||
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
||||||
handle_possess(self, possessor_uid, possesse_uid)
|
handle_possess(self, possessor_uid, possesse_uid)
|
||||||
},
|
},
|
||||||
|
12
server/src/sys/agent.rs
Normal file → Executable file
12
server/src/sys/agent.rs
Normal file → Executable file
@ -68,9 +68,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
read_data.light_emitter.maybe(),
|
read_data.light_emitter.maybe(),
|
||||||
read_data.groups.maybe(),
|
read_data.groups.maybe(),
|
||||||
read_data.rtsim_entities.maybe(),
|
read_data.rtsim_entities.maybe(),
|
||||||
!&read_data.is_mounts,
|
(
|
||||||
read_data.is_riders.maybe(),
|
!&read_data.is_mounts,
|
||||||
read_data.is_volume_riders.maybe(),
|
read_data.is_riders.maybe(),
|
||||||
|
read_data.is_volume_riders.maybe(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
@ -93,9 +95,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
light_emitter,
|
light_emitter,
|
||||||
group,
|
group,
|
||||||
rtsim_entity,
|
rtsim_entity,
|
||||||
_,
|
(_, is_rider, is_volume_rider),
|
||||||
is_rider,
|
|
||||||
is_volume_rider,
|
|
||||||
)| {
|
)| {
|
||||||
let mut event_emitter = event_bus.emitter();
|
let mut event_emitter = event_bus.emitter();
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
37
server/src/sys/agent/behavior_tree.rs
Normal file → Executable file
37
server/src/sys/agent/behavior_tree.rs
Normal file → Executable file
@ -25,7 +25,7 @@ use self::interaction::{
|
|||||||
use super::{
|
use super::{
|
||||||
consts::{
|
consts::{
|
||||||
DAMAGE_MEMORY_DURATION, FLEE_DURATION, HEALING_ITEM_THRESHOLD, MAX_PATROL_DIST,
|
DAMAGE_MEMORY_DURATION, FLEE_DURATION, HEALING_ITEM_THRESHOLD, MAX_PATROL_DIST,
|
||||||
NORMAL_FLEE_DIR_DIST, NPC_PICKUP_RANGE, RETARGETING_THRESHOLD_SECONDS,
|
MAX_STAY_DISTANCE, NORMAL_FLEE_DIR_DIST, NPC_PICKUP_RANGE, RETARGETING_THRESHOLD_SECONDS,
|
||||||
STD_AWARENESS_DECAY_RATE,
|
STD_AWARENESS_DECAY_RATE,
|
||||||
},
|
},
|
||||||
data::{AgentData, ReadData, TargetData},
|
data::{AgentData, ReadData, TargetData},
|
||||||
@ -406,13 +406,30 @@ fn do_pickup_loot(bdata: &mut BehaviorData) -> bool {
|
|||||||
fn follow_if_far_away(bdata: &mut BehaviorData) -> bool {
|
fn follow_if_far_away(bdata: &mut BehaviorData) -> bool {
|
||||||
if let Some(Target { target, .. }) = bdata.agent.target {
|
if let Some(Target { target, .. }) = bdata.agent.target {
|
||||||
if let Some(tgt_pos) = bdata.read_data.positions.get(target) {
|
if let Some(tgt_pos) = bdata.read_data.positions.get(target) {
|
||||||
let dist_sqrd = bdata.agent_data.pos.0.distance_squared(tgt_pos.0);
|
if let Some(stay_pos) = bdata.agent.stay_pos {
|
||||||
|
let distance_from_stay = stay_pos.0.distance_squared(bdata.agent_data.pos.0);
|
||||||
if dist_sqrd > (MAX_PATROL_DIST * bdata.agent.psyche.idle_wander_factor).powi(2) {
|
bdata.controller.push_action(ControlAction::Sit);
|
||||||
bdata
|
if distance_from_stay > (MAX_STAY_DISTANCE).powi(2) {
|
||||||
.agent_data
|
bdata.agent_data.follow(
|
||||||
.follow(bdata.agent, bdata.controller, bdata.read_data, tgt_pos);
|
bdata.agent,
|
||||||
return true;
|
bdata.controller,
|
||||||
|
bdata.read_data,
|
||||||
|
&stay_pos,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bdata.controller.push_action(ControlAction::Stand);
|
||||||
|
let dist_sqrd = bdata.agent_data.pos.0.distance_squared(tgt_pos.0);
|
||||||
|
if dist_sqrd > (MAX_PATROL_DIST * bdata.agent.psyche.idle_wander_factor).powi(2) {
|
||||||
|
bdata.agent_data.follow(
|
||||||
|
bdata.agent,
|
||||||
|
bdata.controller,
|
||||||
|
bdata.read_data,
|
||||||
|
tgt_pos,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,8 +448,8 @@ fn attack_if_owner_hurt(bdata: &mut BehaviorData) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
let stay = bdata.agent.stay_pos.is_some();
|
||||||
if owner_recently_attacked {
|
if owner_recently_attacked && !stay {
|
||||||
bdata.agent_data.attack_target_attacker(
|
bdata.agent_data.attack_target_attacker(
|
||||||
bdata.agent,
|
bdata.agent,
|
||||||
bdata.read_data,
|
bdata.read_data,
|
||||||
|
8
server/src/sys/pets.rs
Normal file → Executable file
8
server/src/sys/pets.rs
Normal file → Executable file
@ -1,5 +1,5 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Alignment, Pet, PhysicsState, Pos},
|
comp::{Agent, Alignment, Pet, PhysicsState, Pos},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::IdMaps,
|
uid::IdMaps,
|
||||||
};
|
};
|
||||||
@ -16,6 +16,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Alignment>,
|
ReadStorage<'a, Alignment>,
|
||||||
ReadStorage<'a, Pet>,
|
ReadStorage<'a, Pet>,
|
||||||
|
ReadStorage<'a, Agent>,
|
||||||
ReadStorage<'a, PhysicsState>,
|
ReadStorage<'a, PhysicsState>,
|
||||||
Read<'a, IdMaps>,
|
Read<'a, IdMaps>,
|
||||||
);
|
);
|
||||||
@ -26,7 +27,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(entities, terrain, mut positions, alignments, pets, physics, id_maps): Self::SystemData,
|
(entities, terrain, mut positions, alignments, pets, agn, physics, id_maps): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
const LOST_PET_DISTANCE_THRESHOLD: f32 = 200.0;
|
const LOST_PET_DISTANCE_THRESHOLD: f32 = 200.0;
|
||||||
|
|
||||||
@ -57,7 +58,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (pet_entity, owner_pos) in lost_pets.iter() {
|
for (pet_entity, owner_pos) in lost_pets.iter() {
|
||||||
if let Some(mut pet_pos) = positions.get_mut(*pet_entity) {
|
let stay = agn.get(*pet_entity).and_then(|x| x.stay_pos).is_some();
|
||||||
|
if let Some(mut pet_pos) = positions.get_mut(*pet_entity) && !stay{
|
||||||
// Move the pets to their owner's position
|
// Move the pets to their owner's position
|
||||||
// TODO: Create a teleportation event to handle this instead of
|
// TODO: Create a teleportation event to handle this instead of
|
||||||
// processing the entity position move here
|
// processing the entity position move here
|
||||||
|
@ -81,6 +81,8 @@ pub enum GameInput {
|
|||||||
ToggleLantern,
|
ToggleLantern,
|
||||||
#[strum(serialize = "gameinput-mount")]
|
#[strum(serialize = "gameinput-mount")]
|
||||||
Mount,
|
Mount,
|
||||||
|
#[strum(serialize = "gameinput-stayfollow")]
|
||||||
|
StayFollow,
|
||||||
#[strum(serialize = "gameinput-chat")]
|
#[strum(serialize = "gameinput-chat")]
|
||||||
Chat,
|
Chat,
|
||||||
#[strum(serialize = "gameinput-command")]
|
#[strum(serialize = "gameinput-command")]
|
||||||
|
39
voxygen/src/hud/mod.rs
Normal file → Executable file
39
voxygen/src/hud/mod.rs
Normal file → Executable file
@ -1505,6 +1505,7 @@ impl Hud {
|
|||||||
let is_mounts = ecs.read_storage::<Is<Mount>>();
|
let is_mounts = ecs.read_storage::<Is<Mount>>();
|
||||||
let is_riders = ecs.read_storage::<Is<Rider>>();
|
let is_riders = ecs.read_storage::<Is<Rider>>();
|
||||||
let stances = ecs.read_storage::<comp::Stance>();
|
let stances = ecs.read_storage::<comp::Stance>();
|
||||||
|
let char_activities = ecs.read_storage::<comp::CharacterActivity>();
|
||||||
let time = ecs.read_resource::<Time>();
|
let time = ecs.read_resource::<Time>();
|
||||||
|
|
||||||
// Check if there was a persistence load error of the skillset, and if so
|
// Check if there was a persistence load error of the skillset, and if so
|
||||||
@ -2254,7 +2255,6 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let speech_bubbles = &self.speech_bubbles;
|
let speech_bubbles = &self.speech_bubbles;
|
||||||
|
|
||||||
// Render overhead name tags and health bars
|
// Render overhead name tags and health bars
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
@ -2272,6 +2272,7 @@ impl Hud {
|
|||||||
dist_sqr,
|
dist_sqr,
|
||||||
alignment,
|
alignment,
|
||||||
is_mount,
|
is_mount,
|
||||||
|
character_activity,
|
||||||
) in (
|
) in (
|
||||||
&entities,
|
&entities,
|
||||||
&pos,
|
&pos,
|
||||||
@ -2286,6 +2287,7 @@ impl Hud {
|
|||||||
&mut hp_floater_lists,
|
&mut hp_floater_lists,
|
||||||
&uids,
|
&uids,
|
||||||
&inventories,
|
&inventories,
|
||||||
|
char_activities.maybe(),
|
||||||
poises.maybe(),
|
poises.maybe(),
|
||||||
(
|
(
|
||||||
alignments.maybe(),
|
alignments.maybe(),
|
||||||
@ -2314,6 +2316,7 @@ impl Hud {
|
|||||||
hpfl,
|
hpfl,
|
||||||
uid,
|
uid,
|
||||||
inventory,
|
inventory,
|
||||||
|
character_activity,
|
||||||
poise,
|
poise,
|
||||||
(alignment, is_mount, is_rider, stance),
|
(alignment, is_mount, is_rider, stance),
|
||||||
)| {
|
)| {
|
||||||
@ -2322,6 +2325,7 @@ impl Hud {
|
|||||||
let in_group = client.group_members().contains_key(uid);
|
let in_group = client.group_members().contains_key(uid);
|
||||||
let is_me = entity == me;
|
let is_me = entity == me;
|
||||||
let dist_sqr = pos.distance_squared(player_pos);
|
let dist_sqr = pos.distance_squared(player_pos);
|
||||||
|
|
||||||
// Determine whether to display nametag and healthbar based on whether the
|
// Determine whether to display nametag and healthbar based on whether the
|
||||||
// entity is mounted, has been damaged, is targeted/selected, or is in your
|
// entity is mounted, has been damaged, is targeted/selected, or is in your
|
||||||
// group
|
// group
|
||||||
@ -2375,8 +2379,22 @@ impl Hud {
|
|||||||
};
|
};
|
||||||
(info.is_some() || bubble.is_some()).then_some({
|
(info.is_some() || bubble.is_some()).then_some({
|
||||||
(
|
(
|
||||||
entity, pos, info, bubble, stats, skill_set, health, buffs, scale,
|
entity,
|
||||||
body, hpfl, in_group, dist_sqr, alignment, is_mount,
|
pos,
|
||||||
|
info,
|
||||||
|
bubble,
|
||||||
|
stats,
|
||||||
|
skill_set,
|
||||||
|
health,
|
||||||
|
buffs,
|
||||||
|
scale,
|
||||||
|
body,
|
||||||
|
hpfl,
|
||||||
|
in_group,
|
||||||
|
dist_sqr,
|
||||||
|
alignment,
|
||||||
|
is_mount,
|
||||||
|
character_activity,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -2429,8 +2447,21 @@ impl Hud {
|
|||||||
options.push((
|
options.push((
|
||||||
GameInput::Mount,
|
GameInput::Mount,
|
||||||
i18n.get_msg("hud-mount").to_string(),
|
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(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
options
|
options
|
||||||
},
|
},
|
||||||
|
@ -13,14 +13,14 @@ use vek::*;
|
|||||||
|
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
|
||||||
comp::{
|
comp::{
|
||||||
|
self,
|
||||||
dialogue::Subject,
|
dialogue::Subject,
|
||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{EquipSlot, Slot},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{tool::ToolKind, ItemDesc},
|
item::{tool::ToolKind, ItemDesc},
|
||||||
ChatType, Content, InputKind, InventoryUpdateEvent, Pos, PresenceKind, Stats,
|
CharacterActivity, ChatType, Content, InputKind, InventoryUpdateEvent, Pos, PresenceKind,
|
||||||
UtteranceKind, Vel,
|
Stats, UtteranceKind, Vel,
|
||||||
},
|
},
|
||||||
consts::MAX_MOUNT_RANGE,
|
consts::MAX_MOUNT_RANGE,
|
||||||
event::UpdateCharacterMetadata,
|
event::UpdateCharacterMetadata,
|
||||||
@ -32,6 +32,7 @@ use common::{
|
|||||||
trade::TradeResult,
|
trade::TradeResult,
|
||||||
util::{Dir, Plane},
|
util::{Dir, Plane},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
|
CachedSpatialGrid,
|
||||||
};
|
};
|
||||||
use common_base::{prof_span, span};
|
use common_base::{prof_span, span};
|
||||||
use common_net::{msg::server::InviteAnswer, sync::WorldSyncExt};
|
use common_net::{msg::server::InviteAnswer, sync::WorldSyncExt};
|
||||||
@ -938,6 +939,45 @@ impl PlayState for SessionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
GameInput::StayFollow if state => {
|
||||||
|
let mut client = self.client.borrow_mut();
|
||||||
|
let player_pos = client
|
||||||
|
.state()
|
||||||
|
.read_storage::<Pos>()
|
||||||
|
.get(client.entity())
|
||||||
|
.copied();
|
||||||
|
|
||||||
|
let mut close_pet = None;
|
||||||
|
if let Some(player_pos) = player_pos {
|
||||||
|
let positions = client.state().read_storage::<Pos>();
|
||||||
|
close_pet = client.state().ecs().read_resource::<CachedSpatialGrid>().0
|
||||||
|
.in_circle_aabr(player_pos.0.xy(), MAX_MOUNT_RANGE)
|
||||||
|
.filter(|e|
|
||||||
|
*e != client.entity()
|
||||||
|
)
|
||||||
|
.filter(|e|
|
||||||
|
matches!(client.state().ecs().read_storage::<comp::Alignment>().get(*e),
|
||||||
|
Some(comp::Alignment::Owned(owner)) if Some(*owner) == client.uid())
|
||||||
|
)
|
||||||
|
.filter(|e|
|
||||||
|
client.state().ecs().read_storage::<Is<Mount>>().get(*e).is_none()
|
||||||
|
)
|
||||||
|
.min_by_key(|e| {
|
||||||
|
OrderedFloat(positions
|
||||||
|
.get(*e)
|
||||||
|
.map_or(MAX_MOUNT_RANGE * MAX_MOUNT_RANGE, |x| {
|
||||||
|
player_pos.0.distance_squared(x.0)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(pet_entity) = close_pet && client.state().read_storage::<Is<Mount>>().get(pet_entity).is_none() {
|
||||||
|
let is_staying = client.state()
|
||||||
|
.read_component_copied::<CharacterActivity>(pet_entity)
|
||||||
|
.map_or(false, |activity| activity.is_pet_staying);
|
||||||
|
client.set_pet_stay(pet_entity, !is_staying);
|
||||||
|
}
|
||||||
|
},
|
||||||
GameInput::Interact => {
|
GameInput::Interact => {
|
||||||
if state {
|
if state {
|
||||||
let mut client = self.client.borrow_mut();
|
let mut client = self.client.borrow_mut();
|
||||||
|
@ -148,6 +148,7 @@ impl ControlSettings {
|
|||||||
GameInput::Sneak => Some(KeyMouse::Key(VirtualKeyCode::LShift)),
|
GameInput::Sneak => Some(KeyMouse::Key(VirtualKeyCode::LShift)),
|
||||||
GameInput::ToggleLantern => Some(KeyMouse::Key(VirtualKeyCode::G)),
|
GameInput::ToggleLantern => Some(KeyMouse::Key(VirtualKeyCode::G)),
|
||||||
GameInput::Mount => Some(KeyMouse::Key(VirtualKeyCode::F)),
|
GameInput::Mount => Some(KeyMouse::Key(VirtualKeyCode::F)),
|
||||||
|
GameInput::StayFollow => Some(KeyMouse::Key(VirtualKeyCode::V)),
|
||||||
GameInput::Map => Some(KeyMouse::Key(VirtualKeyCode::M)),
|
GameInput::Map => Some(KeyMouse::Key(VirtualKeyCode::M)),
|
||||||
GameInput::Bag => Some(KeyMouse::Key(VirtualKeyCode::B)),
|
GameInput::Bag => Some(KeyMouse::Key(VirtualKeyCode::B)),
|
||||||
GameInput::Trade => Some(KeyMouse::Key(VirtualKeyCode::T)),
|
GameInput::Trade => Some(KeyMouse::Key(VirtualKeyCode::T)),
|
||||||
|
@ -320,6 +320,7 @@ pub mod con_settings {
|
|||||||
pub sneak: Button,
|
pub sneak: Button,
|
||||||
pub toggle_lantern: Button,
|
pub toggle_lantern: Button,
|
||||||
pub mount: Button,
|
pub mount: Button,
|
||||||
|
pub stayfollow: Button,
|
||||||
pub map: Button,
|
pub map: Button,
|
||||||
pub bag: Button,
|
pub bag: Button,
|
||||||
pub quest_log: Button,
|
pub quest_log: Button,
|
||||||
@ -424,6 +425,7 @@ pub mod con_settings {
|
|||||||
sneak: Button::Simple(GilButton::LeftThumb),
|
sneak: Button::Simple(GilButton::LeftThumb),
|
||||||
toggle_lantern: Button::Simple(GilButton::Unknown),
|
toggle_lantern: Button::Simple(GilButton::Unknown),
|
||||||
mount: Button::Simple(GilButton::South),
|
mount: Button::Simple(GilButton::South),
|
||||||
|
stayfollow: Button::Simple(GilButton::Unknown),
|
||||||
map: Button::Simple(GilButton::Unknown),
|
map: Button::Simple(GilButton::Unknown),
|
||||||
bag: Button::Simple(GilButton::East),
|
bag: Button::Simple(GilButton::East),
|
||||||
quest_log: Button::Simple(GilButton::Unknown),
|
quest_log: Button::Simple(GilButton::Unknown),
|
||||||
|
Loading…
Reference in New Issue
Block a user