mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add stay/follow option for pets
This commit is contained in:
parent
c6e9d3a202
commit
e611d695b1
@ -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-stay = Stay
|
||||||
|
hud-follow = Follow
|
||||||
hud-sit = Sit
|
hud-sit = Sit
|
||||||
hud-steer = Steer
|
hud-steer = Steer
|
||||||
|
@ -1477,6 +1477,12 @@ 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 toggle_stay(&mut self, entity: EcsEntity){
|
||||||
|
if let Some(uid) = self.state.read_component_copied(entity) {
|
||||||
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::ToggleStay(uid)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn respawn(&mut self) {
|
pub fn respawn(&mut self) {
|
||||||
if self
|
if self
|
||||||
.state
|
.state
|
||||||
|
@ -46,6 +46,7 @@ macro_rules! synced_components {
|
|||||||
beam_segment: BeamSegment,
|
beam_segment: BeamSegment,
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
stance: Stance,
|
stance: Stance,
|
||||||
|
pet_state: PetStates,
|
||||||
// TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum
|
// TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum
|
||||||
// from other entities (e.g. just keys needed to show appearance
|
// from other entities (e.g. just keys needed to show appearance
|
||||||
// based on their loadout). Also, it looks like this actually has
|
// based on their loadout). Also, it looks like this actually has
|
||||||
@ -75,7 +76,7 @@ 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};
|
||||||
|
use common::comp::pet::PetState;
|
||||||
// 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!`.
|
||||||
@ -85,6 +86,7 @@ macro_rules! reexport_comps {
|
|||||||
pub type IsMount = Is<Mount>;
|
pub type IsMount = Is<Mount>;
|
||||||
pub type IsRider = Is<Rider>;
|
pub type IsRider = Is<Rider>;
|
||||||
pub type IsVolumeRider = Is<VolumeRider>;
|
pub type IsVolumeRider = Is<VolumeRider>;
|
||||||
|
pub type PetStates = PetState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-export all the component types. So that uses of `synced_components!` outside this
|
// Re-export all the component types. So that uses of `synced_components!` outside this
|
||||||
@ -105,7 +107,11 @@ synced_components!(reexport_comps);
|
|||||||
|
|
||||||
use crate::sync::{NetSync, SyncFrom};
|
use crate::sync::{NetSync, SyncFrom};
|
||||||
|
|
||||||
|
|
||||||
// These are synced from any entity within range.
|
// These are synced from any entity within range.
|
||||||
|
impl NetSync for PetStates {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
impl NetSync for Body {
|
impl NetSync for Body {
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
@ -146,6 +146,7 @@ pub enum ControlEvent {
|
|||||||
Mount(Uid),
|
Mount(Uid),
|
||||||
MountVolume(VolumePos),
|
MountVolume(VolumePos),
|
||||||
Unmount,
|
Unmount,
|
||||||
|
ToggleStay(Uid),
|
||||||
InventoryEvent(InventoryEvent),
|
InventoryEvent(InventoryEvent),
|
||||||
GroupManip(GroupManip),
|
GroupManip(GroupManip),
|
||||||
RemoveBuff(BuffKind),
|
RemoveBuff(BuffKind),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::comp::{body::Body, phys::Mass, quadruped_medium, quadruped_small};
|
use crate::comp::{body::Body, phys::Mass, quadruped_medium, quadruped_small};
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
use specs::Component;
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
use std::{num::NonZeroU64, sync::Arc};
|
use std::{num::NonZeroU64, sync::Arc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub type PetId = AtomicCell<Option<NonZeroU64>>;
|
pub type PetId = AtomicCell<Option<NonZeroU64>>;
|
||||||
|
|
||||||
@ -107,3 +108,33 @@ impl Component for Pet {
|
|||||||
// isn't worth using `DenseVecStorage` here.
|
// isn't worth using `DenseVecStorage` here.
|
||||||
type Storage = specs::VecStorage<Self>;
|
type Storage = specs::VecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum StayFollow{
|
||||||
|
Stay,
|
||||||
|
Follow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct PetState{
|
||||||
|
pub stay: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PetState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
stay: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PetState {
|
||||||
|
pub fn get_state(&self) -> bool{
|
||||||
|
self.stay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for PetState {
|
||||||
|
type Storage = DerefFlaggedStorage<Self>;
|
||||||
|
}
|
||||||
|
@ -200,6 +200,7 @@ pub enum ServerEvent {
|
|||||||
Mount(EcsEntity, EcsEntity),
|
Mount(EcsEntity, EcsEntity),
|
||||||
MountVolume(EcsEntity, VolumePos),
|
MountVolume(EcsEntity, VolumePos),
|
||||||
Unmount(EcsEntity),
|
Unmount(EcsEntity),
|
||||||
|
ToggleStay(EcsEntity),
|
||||||
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 {
|
||||||
|
@ -203,6 +203,7 @@ impl State {
|
|||||||
ecs.register::<Is<Mount>>();
|
ecs.register::<Is<Mount>>();
|
||||||
ecs.register::<Is<Rider>>();
|
ecs.register::<Is<Rider>>();
|
||||||
ecs.register::<Is<VolumeRider>>();
|
ecs.register::<Is<VolumeRider>>();
|
||||||
|
ecs.register::<comp::pet::PetState>();
|
||||||
ecs.register::<comp::Mass>();
|
ecs.register::<comp::Mass>();
|
||||||
ecs.register::<comp::Density>();
|
ecs.register::<comp::Density>();
|
||||||
ecs.register::<comp::Collider>();
|
ecs.register::<comp::Collider>();
|
||||||
|
@ -63,6 +63,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ControlEvent::ToggleStay(pet_uid) => {
|
||||||
|
if let Some(pet_entity) = read_data
|
||||||
|
.uid_allocator
|
||||||
|
.retrieve_entity_internal(pet_uid.id()){
|
||||||
|
server_emitter.emit(ServerEvent::ToggleStay(pet_entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
ControlEvent::RemoveBuff(buff_id) => {
|
ControlEvent::RemoveBuff(buff_id) => {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
server_emitter.emit(ServerEvent::Buff {
|
||||||
entity,
|
entity,
|
||||||
|
@ -14,7 +14,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory,
|
ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory,
|
||||||
LightEmitter, LootOwner, Ori, PhysicsState, Poise, Pos, Presence, Scale, SkillSet, Stance,
|
LightEmitter, LootOwner, Ori, PhysicsState, Poise, Pos, Presence, Scale, SkillSet, Stance,
|
||||||
Stats, Vel,
|
Stats, Vel, pet::PetState,
|
||||||
},
|
},
|
||||||
consts::GRAVITY,
|
consts::GRAVITY,
|
||||||
link::Is,
|
link::Is,
|
||||||
@ -52,6 +52,7 @@ pub struct AgentData<'a> {
|
|||||||
pub light_emitter: Option<&'a LightEmitter>,
|
pub light_emitter: Option<&'a LightEmitter>,
|
||||||
pub glider_equipped: bool,
|
pub glider_equipped: bool,
|
||||||
pub is_gliding: bool,
|
pub is_gliding: bool,
|
||||||
|
pub is_stay: bool,
|
||||||
pub health: Option<&'a Health>,
|
pub health: Option<&'a Health>,
|
||||||
pub char_state: &'a CharacterState,
|
pub char_state: &'a CharacterState,
|
||||||
pub active_abilities: &'a ActiveAbilities,
|
pub active_abilities: &'a ActiveAbilities,
|
||||||
@ -347,6 +348,7 @@ pub struct ReadData<'a> {
|
|||||||
pub is_mounts: ReadStorage<'a, Is<Mount>>,
|
pub is_mounts: ReadStorage<'a, Is<Mount>>,
|
||||||
pub is_riders: ReadStorage<'a, Is<Rider>>,
|
pub is_riders: ReadStorage<'a, Is<Rider>>,
|
||||||
pub is_volume_riders: ReadStorage<'a, Is<VolumeRider>>,
|
pub is_volume_riders: ReadStorage<'a, Is<VolumeRider>>,
|
||||||
|
pub pet_states: ReadStorage<'a, PetState>,
|
||||||
pub time_of_day: Read<'a, TimeOfDay>,
|
pub time_of_day: Read<'a, TimeOfDay>,
|
||||||
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
|
@ -10,7 +10,7 @@ use common::{
|
|||||||
inventory::slot::EquipSlot,
|
inventory::slot::EquipSlot,
|
||||||
item::{flatten_counted_items, MaterialStatManifest},
|
item::{flatten_counted_items, MaterialStatManifest},
|
||||||
loot_owner::LootOwnerKind,
|
loot_owner::LootOwnerKind,
|
||||||
pet::is_mountable,
|
pet::{is_mountable, PetState},
|
||||||
tool::{AbilityMap, ToolKind},
|
tool::{AbilityMap, ToolKind},
|
||||||
Inventory, LootOwner, Pos, SkillGroupKind,
|
Inventory, LootOwner, Pos, SkillGroupKind,
|
||||||
},
|
},
|
||||||
@ -202,6 +202,18 @@ 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_toggle_stay(server: &mut Server, pet: EcsEntity){
|
||||||
|
let state = server.state_mut();
|
||||||
|
if state.ecs()
|
||||||
|
.read_storage::<PetState>()
|
||||||
|
.get(pet)
|
||||||
|
.map_or(false, |s| s.stay){
|
||||||
|
let _ = state.ecs().write_storage::<PetState>().insert(pet, PetState { stay: false });
|
||||||
|
} else {
|
||||||
|
let _ = state.ecs().write_storage::<PetState>().insert(pet, PetState { stay: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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_sound, handle_unmount, handle_toggle_stay,
|
||||||
};
|
};
|
||||||
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,7 @@ 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::ToggleStay(pet) => handle_toggle_stay(self, pet),
|
||||||
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)
|
||||||
},
|
},
|
||||||
|
@ -71,6 +71,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
!&read_data.is_mounts,
|
!&read_data.is_mounts,
|
||||||
read_data.is_riders.maybe(),
|
read_data.is_riders.maybe(),
|
||||||
read_data.is_volume_riders.maybe(),
|
read_data.is_volume_riders.maybe(),
|
||||||
|
read_data.pet_states.maybe(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
@ -96,6 +97,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
_,
|
_,
|
||||||
is_rider,
|
is_rider,
|
||||||
is_volume_rider,
|
is_volume_rider,
|
||||||
|
pet_state,
|
||||||
)| {
|
)| {
|
||||||
let mut event_emitter = event_bus.emitter();
|
let mut event_emitter = event_bus.emitter();
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
@ -158,6 +160,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
.map_or(false, |item| {
|
.map_or(false, |item| {
|
||||||
matches!(&*item.kind(), comp::item::ItemKind::Glider)
|
matches!(&*item.kind(), comp::item::ItemKind::Glider)
|
||||||
});
|
});
|
||||||
|
let is_stay = pet_state
|
||||||
|
.map_or(false, |s| s.stay);
|
||||||
|
|
||||||
let is_gliding = matches!(
|
let is_gliding = matches!(
|
||||||
read_data.char_states.get(entity),
|
read_data.char_states.get(entity),
|
||||||
@ -229,6 +233,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
light_emitter,
|
light_emitter,
|
||||||
glider_equipped,
|
glider_equipped,
|
||||||
is_gliding,
|
is_gliding,
|
||||||
|
is_stay,
|
||||||
health: read_data.healths.get(entity),
|
health: read_data.healths.get(entity),
|
||||||
char_state,
|
char_state,
|
||||||
active_abilities,
|
active_abilities,
|
||||||
|
@ -407,8 +407,8 @@ 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);
|
let dist_sqrd = bdata.agent_data.pos.0.distance_squared(tgt_pos.0);
|
||||||
|
let stay = bdata.agent_data.is_stay;
|
||||||
if dist_sqrd > (MAX_PATROL_DIST * bdata.agent.psyche.idle_wander_factor).powi(2) {
|
if dist_sqrd > (MAX_PATROL_DIST * bdata.agent.psyche.idle_wander_factor).powi(2) && !stay{
|
||||||
bdata
|
bdata
|
||||||
.agent_data
|
.agent_data
|
||||||
.follow(bdata.agent, bdata.controller, bdata.read_data, tgt_pos);
|
.follow(bdata.agent, bdata.controller, bdata.read_data, tgt_pos);
|
||||||
@ -431,8 +431,8 @@ fn attack_if_owner_hurt(bdata: &mut BehaviorData) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
let stay = bdata.agent_data.is_stay;
|
||||||
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,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Alignment, Pet, PhysicsState, Pos},
|
comp::{Alignment, Pet, PhysicsState, Pos, pet::PetState},
|
||||||
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, PetState>,
|
||||||
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, pet_state, 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 = pet_state.get(*pet_entity).map_or(false, |f| f.stay);
|
||||||
|
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")]
|
||||||
|
@ -1504,6 +1504,7 @@ impl Hud {
|
|||||||
let alignments = ecs.read_storage::<comp::Alignment>();
|
let alignments = ecs.read_storage::<comp::Alignment>();
|
||||||
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 is_stay = ecs.read_storage::<comp::pet::PetState>();
|
||||||
let stances = ecs.read_storage::<comp::Stance>();
|
let stances = ecs.read_storage::<comp::Stance>();
|
||||||
let time = ecs.read_resource::<Time>();
|
let time = ecs.read_resource::<Time>();
|
||||||
|
|
||||||
@ -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,
|
||||||
@ -2317,11 +2317,13 @@ impl Hud {
|
|||||||
poise,
|
poise,
|
||||||
(alignment, is_mount, is_rider, stance),
|
(alignment, is_mount, is_rider, stance),
|
||||||
)| {
|
)| {
|
||||||
|
|
||||||
// Use interpolated position if available
|
// Use interpolated position if available
|
||||||
let pos = interpolated.map_or(pos.0, |i| i.pos);
|
let pos = interpolated.map_or(pos.0, |i| i.pos);
|
||||||
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
|
||||||
@ -2429,8 +2431,33 @@ 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 p = entity;
|
||||||
|
|
||||||
|
let s = is_stay.get(p)
|
||||||
|
.map(|st| st.stay);
|
||||||
|
match s {
|
||||||
|
Some(false) => {
|
||||||
|
options.push((
|
||||||
|
GameInput::StayFollow,
|
||||||
|
i18n.get_msg("hud-stay").to_string(),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
Some(true) => {
|
||||||
|
options.push((
|
||||||
|
GameInput::StayFollow,
|
||||||
|
i18n.get_msg("hud-follow").to_string(),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
options.push((
|
||||||
|
GameInput::StayFollow,
|
||||||
|
i18n.get_msg("hud-stay").to_string(),
|
||||||
|
))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
options
|
options
|
||||||
},
|
},
|
||||||
|
@ -938,6 +938,38 @@ 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();
|
||||||
|
if let Some(player_pos) = player_pos {
|
||||||
|
// Find closest mountable entity
|
||||||
|
let closest_pet = (
|
||||||
|
&client.state().ecs().entities(),
|
||||||
|
&client.state().ecs().read_storage::<Pos>(),
|
||||||
|
// TODO: More cleverly filter by things that can actually be mounted
|
||||||
|
client.state().ecs().read_storage::<comp::Alignment>().maybe(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
.filter(|(entity, _, _)| *entity != client.entity())
|
||||||
|
.filter(|(_, _, alignment)| matches!(alignment, Some(comp::Alignment::Owned(owner)) if Some(*owner) == client.uid()))
|
||||||
|
.map(|(entity, pos, _)| {
|
||||||
|
(entity, player_pos.0.distance_squared(pos.0))
|
||||||
|
})
|
||||||
|
.filter(|(_, dist_sqr)| {
|
||||||
|
*dist_sqr < MAX_MOUNT_RANGE.powi(2)
|
||||||
|
})
|
||||||
|
.min_by_key(|(_, dist_sqr)| OrderedFloat(*dist_sqr));
|
||||||
|
if let Some((pet_entity, _)) = closest_pet
|
||||||
|
{
|
||||||
|
client.toggle_stay(pet_entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
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