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 a new leather pack
|
||||
- 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
|
||||
|
||||
|
@ -33,6 +33,7 @@ gameinput-climbdown = Climb Down
|
||||
gameinput-wallleap = Wall Leap
|
||||
gameinput-togglelantern = Toggle Lantern
|
||||
gameinput-mount = Mount
|
||||
gameinput-stay = Stay/Follow
|
||||
gameinput-chat = Chat
|
||||
gameinput-command = Command
|
||||
gameinput-escape = Escape
|
||||
|
@ -54,5 +54,7 @@ hud-mine-needs_unhandled_case = Needs ???
|
||||
hud-talk = Talk
|
||||
hud-trade = Trade
|
||||
hud-mount = Mount
|
||||
hud-follow = Follow
|
||||
hud-stay= Stay
|
||||
hud-sit = Sit
|
||||
hud-steer = Steer
|
||||
|
@ -1477,6 +1477,14 @@ impl Client {
|
||||
|
||||
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) {
|
||||
if self
|
||||
.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::*;
|
||||
use common::link::Is;
|
||||
use common::mounting::{Mount, Rider, VolumeRider};
|
||||
|
||||
// We alias these because the identifier used for the
|
||||
// component's type is reused as an enum variant name
|
||||
// in the macro's that we pass to `synced_components!`.
|
||||
@ -105,8 +104,6 @@ synced_components!(reexport_comps);
|
||||
|
||||
use crate::sync::{NetSync, SyncFrom};
|
||||
|
||||
// These are synced from any entity within range.
|
||||
|
||||
impl NetSync for Body {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
|
@ -595,6 +595,7 @@ pub struct Agent {
|
||||
/// required and reset each time the flee timer is reset.
|
||||
pub flee_from_pos: Option<Pos>,
|
||||
pub awareness: Awareness,
|
||||
pub stay_pos: Option<Pos>,
|
||||
/// Inputs sent up to rtsim
|
||||
pub rtsim_outbox: Option<VecDeque<NpcInput>>,
|
||||
}
|
||||
@ -700,6 +701,7 @@ impl Agent {
|
||||
sounds_heard: Vec::new(),
|
||||
position_pid_controller: None,
|
||||
flee_from_pos: None,
|
||||
stay_pos: None,
|
||||
awareness: Awareness::new(0.0),
|
||||
rtsim_outbox: None,
|
||||
}
|
||||
|
@ -1004,6 +1004,9 @@ pub struct CharacterActivity {
|
||||
/// `None` means that the look direction should be derived from the
|
||||
/// orientation
|
||||
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 {
|
||||
|
@ -146,6 +146,7 @@ pub enum ControlEvent {
|
||||
Mount(Uid),
|
||||
MountVolume(VolumePos),
|
||||
Unmount,
|
||||
SetPetStay(Uid, bool),
|
||||
InventoryEvent(InventoryEvent),
|
||||
GroupManip(GroupManip),
|
||||
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),
|
||||
MountVolume(EcsEntity, VolumePos),
|
||||
Unmount(EcsEntity),
|
||||
SetPetStay(EcsEntity, EcsEntity, bool),
|
||||
Possess(Uid, Uid),
|
||||
/// Inserts default components for a character when loading into the game
|
||||
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) => {
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity,
|
||||
|
@ -3,6 +3,9 @@ pub const FLEE_DURATION: f32 = 3.0;
|
||||
pub const NPC_PICKUP_RANGE: f32 = 2.5;
|
||||
pub const MAX_PATROL_DIST: f32 = 50.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 SEPARATION_DIST: f32 = 10.0;
|
||||
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},
|
||||
event::EventBus,
|
||||
link::Is,
|
||||
mounting::{Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
||||
mounting::{Mount, Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimVehicle,
|
||||
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))
|
||||
});
|
||||
|
||||
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);
|
||||
let _ = state.link(Mounting {
|
||||
mount: mount_uid,
|
||||
@ -202,6 +210,46 @@ pub fn handle_unmount(server: &mut Server, rider: EcsEntity) {
|
||||
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 {
|
||||
match (player_position, mount_position) {
|
||||
(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 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 invite::{handle_invite, handle_invite_response};
|
||||
@ -144,6 +144,9 @@ impl Server {
|
||||
handle_mount_volume(self, mounter, volume)
|
||||
},
|
||||
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) => {
|
||||
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.groups.maybe(),
|
||||
read_data.rtsim_entities.maybe(),
|
||||
!&read_data.is_mounts,
|
||||
read_data.is_riders.maybe(),
|
||||
read_data.is_volume_riders.maybe(),
|
||||
(
|
||||
!&read_data.is_mounts,
|
||||
read_data.is_riders.maybe(),
|
||||
read_data.is_volume_riders.maybe(),
|
||||
),
|
||||
)
|
||||
.par_join()
|
||||
.for_each_init(
|
||||
@ -93,9 +95,7 @@ impl<'a> System<'a> for Sys {
|
||||
light_emitter,
|
||||
group,
|
||||
rtsim_entity,
|
||||
_,
|
||||
is_rider,
|
||||
is_volume_rider,
|
||||
(_, is_rider, is_volume_rider),
|
||||
)| {
|
||||
let mut event_emitter = event_bus.emitter();
|
||||
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::{
|
||||
consts::{
|
||||
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,
|
||||
},
|
||||
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 {
|
||||
if let Some(Target { target, .. }) = bdata.agent.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 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;
|
||||
if let Some(stay_pos) = bdata.agent.stay_pos {
|
||||
let distance_from_stay = stay_pos.0.distance_squared(bdata.agent_data.pos.0);
|
||||
bdata.controller.push_action(ControlAction::Sit);
|
||||
if distance_from_stay > (MAX_STAY_DISTANCE).powi(2) {
|
||||
bdata.agent_data.follow(
|
||||
bdata.agent,
|
||||
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 {
|
||||
false
|
||||
};
|
||||
|
||||
if owner_recently_attacked {
|
||||
let stay = bdata.agent.stay_pos.is_some();
|
||||
if owner_recently_attacked && !stay {
|
||||
bdata.agent_data.attack_target_attacker(
|
||||
bdata.agent,
|
||||
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::{
|
||||
comp::{Alignment, Pet, PhysicsState, Pos},
|
||||
comp::{Agent, Alignment, Pet, PhysicsState, Pos},
|
||||
terrain::TerrainGrid,
|
||||
uid::IdMaps,
|
||||
};
|
||||
@ -16,6 +16,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Pos>,
|
||||
ReadStorage<'a, Alignment>,
|
||||
ReadStorage<'a, Pet>,
|
||||
ReadStorage<'a, Agent>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
Read<'a, IdMaps>,
|
||||
);
|
||||
@ -26,7 +27,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_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;
|
||||
|
||||
@ -57,7 +58,8 @@ impl<'a> System<'a> for Sys {
|
||||
.collect();
|
||||
|
||||
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
|
||||
// TODO: Create a teleportation event to handle this instead of
|
||||
// processing the entity position move here
|
||||
|
@ -81,6 +81,8 @@ pub enum GameInput {
|
||||
ToggleLantern,
|
||||
#[strum(serialize = "gameinput-mount")]
|
||||
Mount,
|
||||
#[strum(serialize = "gameinput-stayfollow")]
|
||||
StayFollow,
|
||||
#[strum(serialize = "gameinput-chat")]
|
||||
Chat,
|
||||
#[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_riders = ecs.read_storage::<Is<Rider>>();
|
||||
let stances = ecs.read_storage::<comp::Stance>();
|
||||
let char_activities = ecs.read_storage::<comp::CharacterActivity>();
|
||||
let time = ecs.read_resource::<Time>();
|
||||
|
||||
// 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;
|
||||
|
||||
// Render overhead name tags and health bars
|
||||
for (
|
||||
entity,
|
||||
@ -2272,6 +2272,7 @@ impl Hud {
|
||||
dist_sqr,
|
||||
alignment,
|
||||
is_mount,
|
||||
character_activity,
|
||||
) in (
|
||||
&entities,
|
||||
&pos,
|
||||
@ -2286,6 +2287,7 @@ impl Hud {
|
||||
&mut hp_floater_lists,
|
||||
&uids,
|
||||
&inventories,
|
||||
char_activities.maybe(),
|
||||
poises.maybe(),
|
||||
(
|
||||
alignments.maybe(),
|
||||
@ -2314,6 +2316,7 @@ impl Hud {
|
||||
hpfl,
|
||||
uid,
|
||||
inventory,
|
||||
character_activity,
|
||||
poise,
|
||||
(alignment, is_mount, is_rider, stance),
|
||||
)| {
|
||||
@ -2322,6 +2325,7 @@ impl Hud {
|
||||
let in_group = client.group_members().contains_key(uid);
|
||||
let is_me = entity == me;
|
||||
let dist_sqr = pos.distance_squared(player_pos);
|
||||
|
||||
// Determine whether to display nametag and healthbar based on whether the
|
||||
// entity is mounted, has been damaged, is targeted/selected, or is in your
|
||||
// group
|
||||
@ -2375,8 +2379,22 @@ impl Hud {
|
||||
};
|
||||
(info.is_some() || bubble.is_some()).then_some({
|
||||
(
|
||||
entity, pos, info, bubble, stats, skill_set, health, buffs, scale,
|
||||
body, hpfl, in_group, dist_sqr, alignment, is_mount,
|
||||
entity,
|
||||
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((
|
||||
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(),
|
||||
));
|
||||
}
|
||||
options
|
||||
},
|
||||
|
@ -13,14 +13,14 @@ use vek::*;
|
||||
|
||||
use client::{self, Client};
|
||||
use common::{
|
||||
comp,
|
||||
comp::{
|
||||
self,
|
||||
dialogue::Subject,
|
||||
inventory::slot::{EquipSlot, Slot},
|
||||
invite::InviteKind,
|
||||
item::{tool::ToolKind, ItemDesc},
|
||||
ChatType, Content, InputKind, InventoryUpdateEvent, Pos, PresenceKind, Stats,
|
||||
UtteranceKind, Vel,
|
||||
CharacterActivity, ChatType, Content, InputKind, InventoryUpdateEvent, Pos, PresenceKind,
|
||||
Stats, UtteranceKind, Vel,
|
||||
},
|
||||
consts::MAX_MOUNT_RANGE,
|
||||
event::UpdateCharacterMetadata,
|
||||
@ -32,6 +32,7 @@ use common::{
|
||||
trade::TradeResult,
|
||||
util::{Dir, Plane},
|
||||
vol::ReadVol,
|
||||
CachedSpatialGrid,
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
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 => {
|
||||
if state {
|
||||
let mut client = self.client.borrow_mut();
|
||||
|
@ -148,6 +148,7 @@ impl ControlSettings {
|
||||
GameInput::Sneak => Some(KeyMouse::Key(VirtualKeyCode::LShift)),
|
||||
GameInput::ToggleLantern => Some(KeyMouse::Key(VirtualKeyCode::G)),
|
||||
GameInput::Mount => Some(KeyMouse::Key(VirtualKeyCode::F)),
|
||||
GameInput::StayFollow => Some(KeyMouse::Key(VirtualKeyCode::V)),
|
||||
GameInput::Map => Some(KeyMouse::Key(VirtualKeyCode::M)),
|
||||
GameInput::Bag => Some(KeyMouse::Key(VirtualKeyCode::B)),
|
||||
GameInput::Trade => Some(KeyMouse::Key(VirtualKeyCode::T)),
|
||||
|
@ -320,6 +320,7 @@ pub mod con_settings {
|
||||
pub sneak: Button,
|
||||
pub toggle_lantern: Button,
|
||||
pub mount: Button,
|
||||
pub stayfollow: Button,
|
||||
pub map: Button,
|
||||
pub bag: Button,
|
||||
pub quest_log: Button,
|
||||
@ -424,6 +425,7 @@ pub mod con_settings {
|
||||
sneak: Button::Simple(GilButton::LeftThumb),
|
||||
toggle_lantern: Button::Simple(GilButton::Unknown),
|
||||
mount: Button::Simple(GilButton::South),
|
||||
stayfollow: Button::Simple(GilButton::Unknown),
|
||||
map: Button::Simple(GilButton::Unknown),
|
||||
bag: Button::Simple(GilButton::East),
|
||||
quest_log: Button::Simple(GilButton::Unknown),
|
||||
|
Loading…
Reference in New Issue
Block a user