mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Overhauled mounting to make it more reliable
This commit is contained in:
parent
37c14037cb
commit
b3e2d825ed
@ -44,6 +44,8 @@ use common::{
|
||||
trade::{PendingTrade, SitePrices, TradeAction, TradeId, TradeResult},
|
||||
uid::{Uid, UidAllocator},
|
||||
vol::RectVolSize,
|
||||
mounting::Rider,
|
||||
link::Is,
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
use common_net::{
|
||||
@ -1152,10 +1154,10 @@ impl Client {
|
||||
)));
|
||||
}
|
||||
|
||||
pub fn is_mounted(&self) -> bool {
|
||||
pub fn is_riding(&self) -> bool {
|
||||
self.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Mounting>()
|
||||
.read_storage::<Is<Rider>>()
|
||||
.get(self.entity())
|
||||
.is_some()
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
use crate::sync;
|
||||
use common::{comp, resources::Time};
|
||||
use common::{
|
||||
comp,
|
||||
resources::Time,
|
||||
mounting::{Mount, Rider},
|
||||
link::Is,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::WorldExt;
|
||||
use std::marker::PhantomData;
|
||||
@ -27,8 +32,8 @@ sum_type! {
|
||||
Item(comp::Item),
|
||||
Scale(comp::Scale),
|
||||
Group(comp::Group),
|
||||
MountState(comp::MountState),
|
||||
Mounting(comp::Mounting),
|
||||
IsMount(Is<Mount>),
|
||||
IsRider(Is<Rider>),
|
||||
Mass(comp::Mass),
|
||||
Density(comp::Density),
|
||||
Collider(comp::Collider),
|
||||
@ -63,8 +68,8 @@ sum_type! {
|
||||
Item(PhantomData<comp::Item>),
|
||||
Scale(PhantomData<comp::Scale>),
|
||||
Group(PhantomData<comp::Group>),
|
||||
MountState(PhantomData<comp::MountState>),
|
||||
Mounting(PhantomData<comp::Mounting>),
|
||||
IsMount(PhantomData<Is<Mount>>),
|
||||
IsRider(PhantomData<Is<Rider>>),
|
||||
Mass(PhantomData<comp::Mass>),
|
||||
Density(PhantomData<comp::Density>),
|
||||
Collider(PhantomData<comp::Collider>),
|
||||
@ -104,8 +109,8 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Scale(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Group(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::MountState(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Mounting(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::IsMount(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::IsRider(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Density(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Collider(comp) => sync::handle_insert(comp, entity, world),
|
||||
@ -149,8 +154,8 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Scale(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Group(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::MountState(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Mounting(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::IsMount(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::IsRider(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Density(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Collider(comp) => sync::handle_modify(comp, entity, world),
|
||||
@ -193,8 +198,8 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
|
||||
EcsCompPhantom::Scale(_) => sync::handle_remove::<comp::Scale>(entity, world),
|
||||
EcsCompPhantom::Group(_) => sync::handle_remove::<comp::Group>(entity, world),
|
||||
EcsCompPhantom::MountState(_) => sync::handle_remove::<comp::MountState>(entity, world),
|
||||
EcsCompPhantom::Mounting(_) => sync::handle_remove::<comp::Mounting>(entity, world),
|
||||
EcsCompPhantom::IsMount(_) => sync::handle_remove::<Is<Mount>>(entity, world),
|
||||
EcsCompPhantom::IsRider(_) => sync::handle_remove::<Is<Rider>>(entity, world),
|
||||
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
|
||||
EcsCompPhantom::Density(_) => sync::handle_remove::<comp::Density>(entity, world),
|
||||
EcsCompPhantom::Collider(_) => sync::handle_remove::<comp::Collider>(entity, world),
|
||||
|
@ -730,8 +730,8 @@ impl Body {
|
||||
)
|
||||
}
|
||||
|
||||
/// Component of the mounting offset specific to the mountee
|
||||
pub fn mountee_offset(&self) -> Vec3<f32> {
|
||||
/// Component of the mounting offset specific to the mount
|
||||
pub fn mount_offset(&self) -> Vec3<f32> {
|
||||
match self {
|
||||
Body::QuadrupedMedium(quadruped_medium) => {
|
||||
match (quadruped_medium.species, quadruped_medium.body_type) {
|
||||
@ -789,8 +789,8 @@ impl Body {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Component of the mounting offset specific to the mounter
|
||||
pub fn mounter_offset(&self) -> Vec3<f32> {
|
||||
/// Component of the mounting offset specific to the rider
|
||||
pub fn rider_offset(&self) -> Vec3<f32> {
|
||||
match self {
|
||||
Body::Humanoid(_) => [0.0, 0.0, 0.0],
|
||||
_ => [0.0, 0.0, 0.0],
|
||||
|
@ -284,20 +284,3 @@ impl Controller {
|
||||
impl Component for Controller {
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum MountState {
|
||||
Unmounted,
|
||||
MountedBy(Uid),
|
||||
}
|
||||
|
||||
impl Component for MountState {
|
||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Mounting(pub Uid);
|
||||
|
||||
impl Component for Mounting {
|
||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ pub use self::{
|
||||
combo::Combo,
|
||||
controller::{
|
||||
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr,
|
||||
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
||||
InputKind, InventoryAction, InventoryEvent, InventoryManip,
|
||||
UtteranceKind,
|
||||
},
|
||||
energy::Energy,
|
||||
|
@ -12,7 +12,9 @@
|
||||
trait_alias,
|
||||
type_alias_impl_trait,
|
||||
extend_one,
|
||||
arbitrary_enum_discriminant
|
||||
arbitrary_enum_discriminant,
|
||||
generic_associated_types,
|
||||
arbitrary_self_types
|
||||
)]
|
||||
#![feature(hash_drain_filter)]
|
||||
|
||||
@ -78,6 +80,10 @@ pub mod uid;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod vol;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod volumes;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod link;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod mounting;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use cached_spatial_grid::CachedSpatialGrid;
|
||||
|
76
common/src/link.rs
Normal file
76
common/src/link.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{SystemData, Component, DerefFlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::{
|
||||
ops::Deref,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub trait Link: Sized + Send + Sync + 'static {
|
||||
type CreateData<'a>: SystemData<'a>;
|
||||
fn create(this: &LinkHandle<Self>, data: Self::CreateData<'_>) -> Result<(), ()>;
|
||||
|
||||
type PersistData<'a>: SystemData<'a>;
|
||||
fn persist(this: &LinkHandle<Self>, data: Self::PersistData<'_>) -> bool;
|
||||
|
||||
type DeleteData<'a>: SystemData<'a>;
|
||||
fn delete(this: &LinkHandle<Self>, data: Self::DeleteData<'_>);
|
||||
}
|
||||
|
||||
pub trait Role {
|
||||
type Link: Link;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Is<R: Role> {
|
||||
#[serde(bound(serialize = "R::Link: Serialize"))]
|
||||
#[serde(bound(deserialize = "R::Link: Deserialize<'de>"))]
|
||||
link: LinkHandle<R::Link>,
|
||||
}
|
||||
|
||||
impl<R: Role> Is<R> {
|
||||
pub fn delete(&self, data: <R::Link as Link>::DeleteData<'_>) {
|
||||
R::Link::delete(&self.link, data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Role> Clone for Is<R> {
|
||||
fn clone(&self) -> Self { Self { link: self.link.clone() } }
|
||||
}
|
||||
|
||||
impl<R: Role> Deref for Is<R> {
|
||||
type Target = R::Link;
|
||||
fn deref(&self) -> &Self::Target { &self.link }
|
||||
}
|
||||
|
||||
impl<R: Role + 'static> Component for Is<R>
|
||||
where R::Link: Send + Sync + 'static
|
||||
{
|
||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct LinkHandle<L: Link> {
|
||||
link: Arc<L>,
|
||||
}
|
||||
|
||||
impl<L: Link> Clone for LinkHandle<L> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { link: self.link.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Link> LinkHandle<L> {
|
||||
pub fn from_link(link: L) -> Self {
|
||||
Self { link: Arc::new(link) }
|
||||
}
|
||||
|
||||
pub fn make_role<R: Role<Link = L>>(&self) -> Is<R> {
|
||||
Is { link: self.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Link> Deref for LinkHandle<L> {
|
||||
type Target = L;
|
||||
fn deref(&self) -> &Self::Target { &self.link }
|
||||
}
|
119
common/src/mounting.rs
Normal file
119
common/src/mounting.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use crate::{
|
||||
comp,
|
||||
link::{Is, Link, Role, LinkHandle},
|
||||
uid::{Uid, UidAllocator},
|
||||
terrain::TerrainGrid,
|
||||
};
|
||||
use specs::{Entities, ReadStorage, Read, ReadExpect, WriteStorage};
|
||||
use specs::saveload::MarkerAllocator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Rider;
|
||||
|
||||
impl Role for Rider {
|
||||
type Link = Mounting;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Mount;
|
||||
|
||||
impl Role for Mount {
|
||||
type Link = Mounting;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Mounting {
|
||||
pub mount: Uid,
|
||||
pub rider: Uid,
|
||||
}
|
||||
|
||||
impl Link for Mounting {
|
||||
type CreateData<'a> = (
|
||||
Read<'a, UidAllocator>,
|
||||
WriteStorage<'a, Is<Mount>>,
|
||||
WriteStorage<'a, Is<Rider>>,
|
||||
);
|
||||
fn create(this: &LinkHandle<Self>, (uid_allocator, mut is_mounts, mut is_riders): Self::CreateData<'_>) -> Result<(), ()> {
|
||||
let entity = |uid: Uid| uid_allocator
|
||||
.retrieve_entity_internal(uid.into());
|
||||
|
||||
|
||||
if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
|
||||
let can_mount_with = |entity| is_mounts.get(entity).is_none() && is_riders.get(entity).is_none();
|
||||
|
||||
if can_mount_with(mount) && can_mount_with(rider) {
|
||||
let _ = is_mounts.insert(mount, this.make_role());
|
||||
let _ = is_riders.insert(rider, this.make_role());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
type PersistData<'a> = (
|
||||
Read<'a, UidAllocator>,
|
||||
Entities<'a>,
|
||||
ReadStorage<'a, comp::Health>,
|
||||
ReadStorage<'a, Is<Mount>>,
|
||||
ReadStorage<'a, Is<Rider>>,
|
||||
);
|
||||
fn persist(this: &LinkHandle<Self>, (uid_allocator, entities, healths, is_mounts, is_riders): Self::PersistData<'_>) -> bool {
|
||||
let entity = |uid: Uid| uid_allocator
|
||||
.retrieve_entity_internal(uid.into());
|
||||
|
||||
if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
|
||||
let is_alive = |entity| entities.is_alive(entity) && healths.get(entity).map_or(true, |h| !h.is_dead);
|
||||
|
||||
// Ensure that both entities are alive and that they continue to be linked
|
||||
is_alive(mount)
|
||||
&& is_alive(rider)
|
||||
&& is_mounts.get(mount).is_some()
|
||||
&& is_riders.get(rider).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
type DeleteData<'a> = (
|
||||
Read<'a, UidAllocator>,
|
||||
WriteStorage<'a, Is<Mount>>,
|
||||
WriteStorage<'a, Is<Rider>>,
|
||||
WriteStorage<'a, comp::Pos>,
|
||||
WriteStorage<'a, comp::ForceUpdate>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
);
|
||||
fn delete(this: &LinkHandle<Self>, (uid_allocator, mut is_mounts, mut is_riders, mut positions, mut force_update, terrain): Self::DeleteData<'_>) {
|
||||
let entity = |uid: Uid| uid_allocator
|
||||
.retrieve_entity_internal(uid.into());
|
||||
|
||||
let mount = entity(this.mount);
|
||||
let rider = entity(this.rider);
|
||||
|
||||
// Delete link components
|
||||
mount.map(|mount| is_mounts.remove(mount));
|
||||
rider.map(|rider| is_riders.remove(rider));
|
||||
|
||||
// Try to move the rider to a safe place when dismounting
|
||||
let safe_pos = rider
|
||||
.and_then(|rider| positions.get(rider).copied())
|
||||
.filter(|rider_pos| terrain.is_space(rider_pos.0.map(|e| e.floor() as i32)))
|
||||
.or_else(|| mount
|
||||
.and_then(|mount| positions.get(mount).copied())
|
||||
.filter(|mount_pos| terrain.is_space((mount_pos.0 + Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))));
|
||||
rider
|
||||
.and_then(|rider| Some(rider).zip(positions.get_mut(rider)))
|
||||
.map(|(rider, pos)| {
|
||||
let old_pos = pos.0.map(|e| e.floor() as i32);
|
||||
pos.0 = safe_pos
|
||||
.map(|p| p.0.map(|e| e.floor()))
|
||||
.unwrap_or_else(|| terrain.find_space(old_pos).map(|e| e as f32))
|
||||
+ Vec3::new(0.5, 0.5, 0.0);
|
||||
force_update.insert(rider, comp::ForceUpdate);
|
||||
});
|
||||
}
|
||||
}
|
@ -162,19 +162,21 @@ impl TerrainGrid {
|
||||
self.try_find_space(pos).unwrap_or(pos)
|
||||
}
|
||||
|
||||
pub fn is_space(&self, pos: Vec3<i32>) -> bool {
|
||||
(0..2).all(|z| {
|
||||
self.get(pos + Vec3::unit_z() * z)
|
||||
.map_or(true, |b| !b.is_solid())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_find_space(&self, pos: Vec3<i32>) -> Option<Vec3<i32>> {
|
||||
const SEARCH_DIST: i32 = 63;
|
||||
(0..SEARCH_DIST * 2 + 1)
|
||||
.map(|i| if i % 2 == 0 { i } else { -i } / 2)
|
||||
.map(|z_diff| pos + Vec3::unit_z() * z_diff)
|
||||
.find(|test_pos| {
|
||||
self.get(test_pos - Vec3::unit_z())
|
||||
.map_or(false, |b| b.is_filled())
|
||||
&& (0..2).all(|z| {
|
||||
self.get(test_pos + Vec3::unit_z() * z)
|
||||
.map_or(true, |b| !b.is_solid())
|
||||
})
|
||||
})
|
||||
.find(|pos| self.get(pos - Vec3::unit_z())
|
||||
.map_or(false, |b| b.is_filled())
|
||||
&& self.is_space(*pos))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ use common::{
|
||||
time::DayPeriod,
|
||||
trade::Trades,
|
||||
vol::{ReadVol, WriteVol},
|
||||
link::Is,
|
||||
mounting::{Mount, Rider},
|
||||
};
|
||||
use common_base::span;
|
||||
use common_ecs::{PhysicsMetrics, SysMetrics};
|
||||
@ -32,7 +34,10 @@ use specs::{
|
||||
storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage},
|
||||
Component, DispatcherBuilder, Entity as EcsEntity, WorldExt,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
@ -141,8 +146,8 @@ impl State {
|
||||
ecs.register::<comp::LightEmitter>();
|
||||
ecs.register::<comp::Item>();
|
||||
ecs.register::<comp::Scale>();
|
||||
ecs.register::<comp::Mounting>();
|
||||
ecs.register::<comp::MountState>();
|
||||
ecs.register::<Is<Mount>>();
|
||||
ecs.register::<Is<Rider>>();
|
||||
ecs.register::<comp::Mass>();
|
||||
ecs.register::<comp::Density>();
|
||||
ecs.register::<comp::Collider>();
|
||||
|
@ -7,7 +7,7 @@ use common::{
|
||||
comp::{
|
||||
self, character_state::OutputEvents, inventory::item::MaterialStatManifest,
|
||||
ActiveAbilities, Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health,
|
||||
Inventory, InventoryManip, Mass, Melee, Mounting, Ori, PhysicsState, Poise, Pos, SkillSet,
|
||||
Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, SkillSet,
|
||||
StateUpdate, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
@ -19,6 +19,8 @@ use common::{
|
||||
},
|
||||
terrain::TerrainGrid,
|
||||
uid::Uid,
|
||||
mounting::Rider,
|
||||
link::Is,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
|
||||
@ -37,7 +39,7 @@ pub struct ReadData<'a> {
|
||||
melee_attacks: ReadStorage<'a, Melee>,
|
||||
beams: ReadStorage<'a, Beam>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
mountings: ReadStorage<'a, Mounting>,
|
||||
is_riders: ReadStorage<'a, Is<Rider>>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
skill_sets: ReadStorage<'a, SkillSet>,
|
||||
active_abilities: ReadStorage<'a, ActiveAbilities>,
|
||||
@ -110,7 +112,7 @@ impl<'a> System<'a> for Sys {
|
||||
health,
|
||||
body,
|
||||
physics,
|
||||
(stat, skill_set, active_abilities),
|
||||
(stat, skill_set, active_abilities, is_rider),
|
||||
combo,
|
||||
) in (
|
||||
&read_data.entities,
|
||||
@ -131,6 +133,7 @@ impl<'a> System<'a> for Sys {
|
||||
&read_data.stats,
|
||||
&read_data.skill_sets,
|
||||
&read_data.active_abilities,
|
||||
read_data.is_riders.maybe(),
|
||||
),
|
||||
&read_data.combos,
|
||||
)
|
||||
@ -207,7 +210,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Mounted occurs after control actions have been handled
|
||||
// If mounted, character state is controlled by mount
|
||||
if let Some(Mounting(_)) = read_data.mountings.get(entity) {
|
||||
if is_rider.is_some() {
|
||||
let idle_state = CharacterState::Idle(idle::Data { is_sneaking: false });
|
||||
if *join_struct.char_state != idle_state {
|
||||
*join_struct.char_state = idle_state;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![feature(bool_to_option)]
|
||||
#![feature(bool_to_option, let_else)]
|
||||
#![allow(clippy::option_map_unit_fn)]
|
||||
|
||||
mod aura;
|
||||
|
@ -1,11 +1,14 @@
|
||||
use common::{
|
||||
comp::{Body, Controller, MountState, Mounting, Ori, Pos, Vel},
|
||||
comp::{Body, Controller, Ori, Pos, Vel},
|
||||
uid::UidAllocator,
|
||||
mounting::{Mount, Rider},
|
||||
link::Is,
|
||||
uid::Uid,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use specs::{
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
Entities, Join, Read, ReadStorage, WriteStorage,
|
||||
Entities, Join, Read, ReadStorage, WriteStorage, WriteExpect,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
@ -17,9 +20,10 @@ impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, UidAllocator>,
|
||||
Entities<'a>,
|
||||
ReadStorage<'a, Uid>,
|
||||
WriteStorage<'a, Controller>,
|
||||
WriteStorage<'a, MountState>,
|
||||
WriteStorage<'a, Mounting>,
|
||||
ReadStorage<'a, Is<Rider>>,
|
||||
ReadStorage<'a, Is<Mount>>,
|
||||
WriteStorage<'a, Pos>,
|
||||
WriteStorage<'a, Vel>,
|
||||
WriteStorage<'a, Ori>,
|
||||
@ -35,70 +39,49 @@ impl<'a> System<'a> for Sys {
|
||||
(
|
||||
uid_allocator,
|
||||
entities,
|
||||
uids,
|
||||
mut controllers,
|
||||
mut mount_state,
|
||||
mut mountings,
|
||||
is_riders,
|
||||
is_mounts,
|
||||
mut positions,
|
||||
mut velocities,
|
||||
mut orientations,
|
||||
bodies,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
// Mounted entities.
|
||||
for (entity, mut mount_states, body) in (&entities, &mut mount_state, bodies.maybe()).join()
|
||||
{
|
||||
match *mount_states {
|
||||
MountState::Unmounted => {},
|
||||
MountState::MountedBy(mounter_uid) => {
|
||||
// Note: currently controller events are not passed through since none of them
|
||||
// are currently relevant to controlling the mounted entity
|
||||
if let Some((inputs, queued_inputs, mounter)) = uid_allocator
|
||||
.retrieve_entity_internal(mounter_uid.id())
|
||||
.and_then(|mounter| {
|
||||
controllers
|
||||
.get(mounter)
|
||||
.map(|c| (c.inputs.clone(), c.queued_inputs.clone(), mounter))
|
||||
})
|
||||
{
|
||||
// TODO: consider joining on these? (remember we can use .maybe())
|
||||
let pos = positions.get(entity).copied();
|
||||
let ori = orientations.get(entity).copied();
|
||||
let vel = velocities.get(entity).copied();
|
||||
if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
|
||||
let mounter_body = bodies.get(mounter);
|
||||
let mounting_offset = body.map_or(Vec3::unit_z(), Body::mountee_offset)
|
||||
+ mounter_body.map_or(Vec3::zero(), Body::mounter_offset);
|
||||
let _ = positions
|
||||
.insert(mounter, Pos(pos.0 + ori.to_quat() * mounting_offset));
|
||||
let _ = orientations.insert(mounter, ori);
|
||||
let _ = velocities.insert(mounter, vel);
|
||||
}
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
*controller = Controller {
|
||||
inputs,
|
||||
queued_inputs,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*mount_states = MountState::Unmounted;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
// For each mount...
|
||||
for (entity, is_mount, body) in (&entities, &is_mounts, bodies.maybe()).join() {
|
||||
// ...find the rider...
|
||||
let Some((inputs, queued_inputs, rider)) = uid_allocator
|
||||
.retrieve_entity_internal(is_mount.rider.id())
|
||||
.and_then(|rider| {
|
||||
controllers
|
||||
.get(rider)
|
||||
.map(|c| (c.inputs.clone(), c.queued_inputs.clone(), rider))
|
||||
})
|
||||
else { continue };
|
||||
|
||||
let mut to_unmount = Vec::new();
|
||||
for (entity, Mounting(mountee_uid)) in (&entities, &mountings).join() {
|
||||
if uid_allocator
|
||||
.retrieve_entity_internal(mountee_uid.id())
|
||||
.filter(|mountee| entities.is_alive(*mountee))
|
||||
.is_none()
|
||||
{
|
||||
to_unmount.push(entity);
|
||||
// ...apply the mount's position/ori/velocity to the rider...
|
||||
let pos = positions.get(entity).copied();
|
||||
let ori = orientations.get(entity).copied();
|
||||
let vel = velocities.get(entity).copied();
|
||||
if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
|
||||
let mounter_body = bodies.get(rider);
|
||||
let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset)
|
||||
+ mounter_body.map_or(Vec3::zero(), Body::rider_offset);
|
||||
let _ = positions
|
||||
.insert(rider, Pos(pos.0 + ori.to_quat() * mounting_offset));
|
||||
let _ = orientations.insert(rider, ori);
|
||||
let _ = velocities.insert(rider, vel);
|
||||
}
|
||||
// ...and apply the rider's inputs to the mount's controller.
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
*controller = Controller {
|
||||
inputs,
|
||||
queued_inputs,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
for entity in to_unmount {
|
||||
mountings.remove(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use common::{
|
||||
comp::{
|
||||
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
||||
fluid_dynamics::{Fluid, LiquidKind, Wings},
|
||||
Body, CharacterState, Collider, Density, Mass, Mounting, Ori, PhysicsState, Pos,
|
||||
Body, CharacterState, Collider, Density, Mass, Ori, PhysicsState, Pos,
|
||||
PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel,
|
||||
},
|
||||
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
||||
@ -14,6 +14,8 @@ use common::{
|
||||
uid::Uid,
|
||||
util::{Projection, SpatialGrid},
|
||||
vol::{BaseVol, ReadVol},
|
||||
mounting::Rider,
|
||||
link::Is,
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System};
|
||||
@ -111,7 +113,7 @@ pub struct PhysicsRead<'a> {
|
||||
stickies: ReadStorage<'a, Sticky>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
colliders: ReadStorage<'a, Collider>,
|
||||
mountings: ReadStorage<'a, Mounting>,
|
||||
is_ridings: ReadStorage<'a, Is<Rider>>,
|
||||
projectiles: ReadStorage<'a, Projectile>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
@ -169,7 +171,7 @@ impl<'a> PhysicsData<'a> {
|
||||
&self.write.velocities,
|
||||
&self.write.positions,
|
||||
!&self.write.previous_phys_cache,
|
||||
!&self.read.mountings,
|
||||
!&self.read.is_ridings,
|
||||
)
|
||||
.join()
|
||||
.map(|(e, _, _, _, _, _)| e)
|
||||
@ -201,7 +203,7 @@ impl<'a> PhysicsData<'a> {
|
||||
&self.read.colliders,
|
||||
self.read.scales.maybe(),
|
||||
self.read.char_states.maybe(),
|
||||
!&self.read.mountings,
|
||||
!&self.read.is_ridings,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -292,14 +294,13 @@ impl<'a> PhysicsData<'a> {
|
||||
let lg2_large_cell_size = 6;
|
||||
let radius_cutoff = 8;
|
||||
let mut spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
||||
for (entity, pos, phys_cache, _, _, _, _) in (
|
||||
for (entity, pos, phys_cache, _, _, _) in (
|
||||
&read.entities,
|
||||
&write.positions,
|
||||
&write.previous_phys_cache,
|
||||
write.velocities.mask(),
|
||||
!&read.projectiles, // Not needed because they are skipped in the inner loop below
|
||||
!&read.mountings,
|
||||
read.colliders.mask(),
|
||||
!&read.is_ridings,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -328,7 +329,7 @@ impl<'a> PhysicsData<'a> {
|
||||
previous_phys_cache,
|
||||
&read.masses,
|
||||
&read.colliders,
|
||||
!&read.mountings,
|
||||
!&read.is_ridings,
|
||||
read.stickies.maybe(),
|
||||
&mut write.physics_states,
|
||||
// TODO: if we need to avoid collisions for other things consider
|
||||
@ -476,6 +477,7 @@ impl<'a> PhysicsData<'a> {
|
||||
collider_other,
|
||||
*mass,
|
||||
*mass_other,
|
||||
vel,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -570,7 +572,7 @@ impl<'a> PhysicsData<'a> {
|
||||
write.physics_states.mask(),
|
||||
!&write.pos_vel_ori_defers, // This is the one we are adding
|
||||
write.previous_phys_cache.mask(),
|
||||
!&read.mountings,
|
||||
!&read.is_ridings,
|
||||
)
|
||||
.join()
|
||||
.map(|t| (t.0, *t.2, *t.3, *t.4))
|
||||
@ -601,7 +603,7 @@ impl<'a> PhysicsData<'a> {
|
||||
&write.physics_states,
|
||||
&read.masses,
|
||||
&read.densities,
|
||||
!&read.mountings,
|
||||
!&read.is_ridings,
|
||||
)
|
||||
.par_join()
|
||||
.for_each_init(
|
||||
@ -730,7 +732,7 @@ impl<'a> PhysicsData<'a> {
|
||||
&mut write.physics_states,
|
||||
&mut write.pos_vel_ori_defers,
|
||||
previous_phys_cache,
|
||||
!&read.mountings,
|
||||
!&read.is_ridings,
|
||||
)
|
||||
.par_join()
|
||||
.filter(|tuple| tuple.3.is_voxel() == terrain_like_entities)
|
||||
@ -1792,6 +1794,7 @@ fn resolve_e2e_collision(
|
||||
collider_other: &Collider,
|
||||
mass: Mass,
|
||||
mass_other: Mass,
|
||||
vel: &Vel,
|
||||
) -> bool {
|
||||
// Find the distance betwen our collider and
|
||||
// collider we collide with and get vector of pushback.
|
||||
@ -1869,7 +1872,9 @@ fn resolve_e2e_collision(
|
||||
let distance_coefficient = collision_dist - diff.magnitude();
|
||||
let force = ELASTIC_FORCE_COEFFICIENT * distance_coefficient * mass_coefficient;
|
||||
|
||||
*vel_delta += Vec3::from(diff.normalized()) * force * step_delta;
|
||||
let diff = diff.normalized();
|
||||
|
||||
*vel_delta += Vec3::from(diff) * force * step_delta * vel.0.xy().try_normalized().map_or(1.0, |dir| diff.dot(-dir).max(0.0));
|
||||
}
|
||||
|
||||
*collision_registered = true;
|
||||
|
@ -622,8 +622,7 @@ fn handle_make_npc(
|
||||
.create_npc(pos, stats, skill_set, health, poise, inventory, body)
|
||||
.with(alignment)
|
||||
.with(scale)
|
||||
.with(comp::Vel(Vec3::new(0.0, 0.0, 0.0)))
|
||||
.with(comp::MountState::Unmounted);
|
||||
.with(comp::Vel(Vec3::new(0.0, 0.0, 0.0)));
|
||||
|
||||
if let Some(agent) = agent {
|
||||
entity_builder = entity_builder.with(agent);
|
||||
@ -1168,7 +1167,6 @@ fn handle_spawn(
|
||||
body,
|
||||
)
|
||||
.with(comp::Vel(vel))
|
||||
.with(comp::MountState::Unmounted)
|
||||
.with(alignment);
|
||||
|
||||
if ai {
|
||||
@ -1251,7 +1249,6 @@ fn handle_spawn_training_dummy(
|
||||
body,
|
||||
)
|
||||
.with(comp::Vel(vel))
|
||||
.with(comp::MountState::Unmounted)
|
||||
.build();
|
||||
|
||||
server.notify_client(
|
||||
|
@ -19,6 +19,8 @@ use common::{
|
||||
terrain::{Block, SpriteKind},
|
||||
uid::Uid,
|
||||
vol::ReadVol,
|
||||
mounting::{Mount, Rider, Mounting},
|
||||
link::Is,
|
||||
};
|
||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||
|
||||
@ -97,62 +99,50 @@ pub fn handle_npc_interaction(server: &mut Server, interactor: EcsEntity, npc_en
|
||||
}
|
||||
}
|
||||
|
||||
/// FIXME: Make mounting more robust, avoid bidirectional links.
|
||||
pub fn handle_mount(server: &mut Server, mounter: EcsEntity, mountee: EcsEntity) {
|
||||
pub fn handle_mount(server: &mut Server, rider: EcsEntity, mount: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
|
||||
if state
|
||||
.ecs()
|
||||
.read_storage::<comp::Mounting>()
|
||||
.get(mounter)
|
||||
.read_storage::<Is<Rider>>()
|
||||
.get(rider)
|
||||
.is_none()
|
||||
{
|
||||
let not_mounting_yet = matches!(
|
||||
state.ecs().read_storage::<comp::MountState>().get(mountee),
|
||||
Some(comp::MountState::Unmounted)
|
||||
);
|
||||
let not_mounting_yet = state
|
||||
.ecs()
|
||||
.read_storage::<Is<Mount>>()
|
||||
.get(mount)
|
||||
.is_none();
|
||||
|
||||
let within_range = || {
|
||||
let positions = state.ecs().read_storage::<comp::Pos>();
|
||||
within_mounting_range(positions.get(mounter), positions.get(mountee))
|
||||
within_mounting_range(positions.get(rider), positions.get(mount))
|
||||
};
|
||||
let healths = state.ecs().read_storage::<comp::Health>();
|
||||
let alive = |e| healths.get(e).map_or(true, |h| !h.is_dead);
|
||||
|
||||
if not_mounting_yet && within_range() && alive(mounter) && alive(mountee) {
|
||||
if not_mounting_yet && within_range() && alive(rider) && alive(mount) {
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
if let (Some(mounter_uid), Some(mountee_uid)) =
|
||||
(uids.get(mounter).copied(), uids.get(mountee).copied())
|
||||
if let (Some(rider_uid), Some(mount_uid)) =
|
||||
(uids.get(rider).copied(), uids.get(mount).copied())
|
||||
{
|
||||
drop(uids);
|
||||
drop(healths);
|
||||
// We know the entities must exist to be able to look up their UIDs, so these
|
||||
// are guaranteed to work; hence we can ignore possible errors here.
|
||||
state.write_component_ignore_entity_dead(
|
||||
mountee,
|
||||
comp::MountState::MountedBy(mounter_uid),
|
||||
);
|
||||
state.write_component_ignore_entity_dead(mounter, comp::Mounting(mountee_uid));
|
||||
let _ = state.link(Mounting {
|
||||
mount: mount_uid,
|
||||
rider: rider_uid,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_unmount(server: &mut Server, mounter: EcsEntity) {
|
||||
pub fn handle_unmount(server: &mut Server, rider: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
let mountee_entity = state
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Mounting>()
|
||||
.get(mounter)
|
||||
.and_then(|mountee| state.ecs().entity_from_uid(mountee.0.into()));
|
||||
if let Some(mountee_entity) = mountee_entity {
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::MountState>()
|
||||
.get_mut(mountee_entity)
|
||||
.map(|mut ms| *ms = comp::MountState::Unmounted);
|
||||
}
|
||||
state.delete_component::<comp::Mounting>(mounter);
|
||||
.write_storage::<Is<Rider>>()
|
||||
.remove(rider);
|
||||
}
|
||||
|
||||
/// FIXME: This code is dangerous and needs to be refactored. We can't just
|
||||
|
@ -663,6 +663,9 @@ impl Server {
|
||||
// will be processed once handle_events() is called below
|
||||
let disconnect_type = self.disconnect_all_clients_if_requested();
|
||||
|
||||
// Handle entity links (such as mounting)
|
||||
self.state.maintain_links();
|
||||
|
||||
// Handle game events
|
||||
frontend_events.append(&mut self.handle_events());
|
||||
|
||||
|
@ -21,6 +21,8 @@ use common::{
|
||||
resources::{Time, TimeOfDay},
|
||||
slowjob::SlowJobPool,
|
||||
uid::{Uid, UidAllocator},
|
||||
link::{Is, Link, Role, LinkHandle},
|
||||
mounting::Mounting,
|
||||
};
|
||||
use common_net::{
|
||||
msg::{CharacterInfo, PlayerListUpdate, PresenceKind, ServerGeneral},
|
||||
@ -30,7 +32,7 @@ use common_state::State;
|
||||
use rand::prelude::*;
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
|
||||
Join, WorldExt,
|
||||
Join, WorldExt, Component,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use tracing::{trace, warn};
|
||||
@ -110,6 +112,10 @@ pub trait StateExt {
|
||||
fn send_chat(&self, msg: comp::UnresolvedChatMsg);
|
||||
fn notify_players(&self, msg: ServerGeneral);
|
||||
fn notify_in_game_clients(&self, msg: ServerGeneral);
|
||||
/// Create a new link between entities (see [`common::mounting`] for an example).
|
||||
fn link<L: Link>(&mut self, link: L) -> Result<(), ()>;
|
||||
/// Maintain active links between entities
|
||||
fn maintain_links(&mut self);
|
||||
/// Delete an entity, recording the deletion in [`DeletedEntities`]
|
||||
fn delete_entity_recorded(
|
||||
&mut self,
|
||||
@ -294,7 +300,7 @@ impl StateExt for State {
|
||||
.with(comp::Combo::default());
|
||||
|
||||
if mountable {
|
||||
builder = builder.with(comp::MountState::Unmounted);
|
||||
// TODO: Re-add mounting check
|
||||
}
|
||||
builder
|
||||
}
|
||||
@ -825,6 +831,37 @@ impl StateExt for State {
|
||||
}
|
||||
}
|
||||
|
||||
fn link<L: Link>(&mut self, link: L) -> Result<(), ()> {
|
||||
let linker = LinkHandle::from_link(link);
|
||||
|
||||
L::create(
|
||||
&linker,
|
||||
self.ecs().system_data(),
|
||||
)?;
|
||||
|
||||
self.ecs_mut()
|
||||
.entry::<Vec<LinkHandle<L>>>()
|
||||
.or_insert_with(Vec::new)
|
||||
.push(linker);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn maintain_links(&mut self) {
|
||||
fn maintain_link<L: Link>(state: &State) {
|
||||
if let Some(mut handles) = state.ecs().try_fetch_mut::<Vec<LinkHandle<L>>>() {
|
||||
handles.retain(|link| if L::persist(link, state.ecs().system_data()) {
|
||||
true
|
||||
} else {
|
||||
L::delete(link, state.ecs().system_data());
|
||||
false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
maintain_link::<Mounting>(self);
|
||||
}
|
||||
|
||||
fn delete_entity_recorded(
|
||||
&mut self,
|
||||
entity: EcsEntity,
|
||||
|
@ -21,7 +21,7 @@ use common::{
|
||||
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
|
||||
AbilityInput, ActiveAbilities, Agent, Alignment, BehaviorCapability, BehaviorState, Body,
|
||||
CharacterAbility, CharacterState, Combo, ControlAction, ControlEvent, Controller, Energy,
|
||||
Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori,
|
||||
Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, Ori,
|
||||
PhysicsState, Pos, Scale, SkillSet, Stats, UnresolvedChatMsg, UtteranceKind, Vel,
|
||||
},
|
||||
consts::GRAVITY,
|
||||
@ -37,6 +37,8 @@ use common::{
|
||||
uid::{Uid, UidAllocator},
|
||||
util::Dir,
|
||||
vol::ReadVol,
|
||||
mounting::Mount,
|
||||
link::Is,
|
||||
};
|
||||
use common_base::prof_span;
|
||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||
@ -152,7 +154,7 @@ pub struct ReadData<'a> {
|
||||
terrain: ReadExpect<'a, TerrainGrid>,
|
||||
alignments: ReadStorage<'a, Alignment>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
mount_states: ReadStorage<'a, MountState>,
|
||||
is_mounts: ReadStorage<'a, Is<Mount>>,
|
||||
time_of_day: Read<'a, TimeOfDay>,
|
||||
light_emitter: ReadStorage<'a, LightEmitter>,
|
||||
#[cfg(feature = "worldgen")]
|
||||
@ -224,15 +226,9 @@ impl<'a> System<'a> for Sys {
|
||||
&mut controllers,
|
||||
read_data.light_emitter.maybe(),
|
||||
read_data.groups.maybe(),
|
||||
read_data.mount_states.maybe(),
|
||||
!&read_data.is_mounts,
|
||||
)
|
||||
.par_join()
|
||||
.filter(|(_, _, _, _, _, _, _, _, _, _, _, _, mount_state)| {
|
||||
// Skip mounted entities
|
||||
mount_state
|
||||
.map(|ms| *ms == MountState::Unmounted)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.for_each_init(
|
||||
|| {
|
||||
prof_span!(guard, "agent rayon job");
|
||||
|
@ -3,10 +3,12 @@ use common::{
|
||||
comp::{
|
||||
item::{tool::AbilityMap, MaterialStatManifest},
|
||||
ActiveAbilities, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider,
|
||||
Combo, Density, Energy, Group, Health, Inventory, Item, LightEmitter, Mass, MountState,
|
||||
Mounting, Ori, Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky, Vel,
|
||||
Combo, Density, Energy, Group, Health, Inventory, Item, LightEmitter, Mass,
|
||||
Ori, Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky, Vel,
|
||||
},
|
||||
uid::Uid,
|
||||
mounting::{Mount, Rider},
|
||||
link::Is,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use common_net::{
|
||||
@ -56,8 +58,8 @@ pub struct TrackedComps<'a> {
|
||||
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||
pub item: ReadStorage<'a, Item>,
|
||||
pub scale: ReadStorage<'a, Scale>,
|
||||
pub mounting: ReadStorage<'a, Mounting>,
|
||||
pub mount_state: ReadStorage<'a, MountState>,
|
||||
pub is_mount: ReadStorage<'a, Is<Mount>>,
|
||||
pub is_rider: ReadStorage<'a, Is<Rider>>,
|
||||
pub group: ReadStorage<'a, Group>,
|
||||
pub mass: ReadStorage<'a, Mass>,
|
||||
pub density: ReadStorage<'a, Density>,
|
||||
@ -137,11 +139,11 @@ impl<'a> TrackedComps<'a> {
|
||||
.get(entity)
|
||||
.copied()
|
||||
.map(|c| comps.push(c.into()));
|
||||
self.mounting
|
||||
self.is_mount
|
||||
.get(entity)
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
self.mount_state
|
||||
self.is_rider
|
||||
.get(entity)
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
@ -205,8 +207,8 @@ pub struct ReadTrackers<'a> {
|
||||
pub inventory: ReadExpect<'a, UpdateTracker<Inventory>>,
|
||||
pub item: ReadExpect<'a, UpdateTracker<Item>>,
|
||||
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
||||
pub mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
|
||||
pub mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
||||
pub is_mount: ReadExpect<'a, UpdateTracker<Is<Mount>>>,
|
||||
pub is_rider: ReadExpect<'a, UpdateTracker<Is<Rider>>>,
|
||||
pub group: ReadExpect<'a, UpdateTracker<Group>>,
|
||||
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
||||
pub density: ReadExpect<'a, UpdateTracker<Density>>,
|
||||
@ -251,8 +253,8 @@ impl<'a> ReadTrackers<'a> {
|
||||
)
|
||||
.with_component(&comps.uid, &*self.item, &comps.item, filter)
|
||||
.with_component(&comps.uid, &*self.scale, &comps.scale, filter)
|
||||
.with_component(&comps.uid, &*self.mounting, &comps.mounting, filter)
|
||||
.with_component(&comps.uid, &*self.mount_state, &comps.mount_state, filter)
|
||||
.with_component(&comps.uid, &*self.is_mount, &comps.is_mount, filter)
|
||||
.with_component(&comps.uid, &*self.is_rider, &comps.is_rider, filter)
|
||||
.with_component(&comps.uid, &*self.group, &comps.group, filter)
|
||||
.with_component(&comps.uid, &*self.mass, &comps.mass, filter)
|
||||
.with_component(&comps.uid, &*self.density, &comps.density, filter)
|
||||
@ -290,8 +292,8 @@ pub struct WriteTrackers<'a> {
|
||||
light_emitter: WriteExpect<'a, UpdateTracker<LightEmitter>>,
|
||||
item: WriteExpect<'a, UpdateTracker<Item>>,
|
||||
scale: WriteExpect<'a, UpdateTracker<Scale>>,
|
||||
mounting: WriteExpect<'a, UpdateTracker<Mounting>>,
|
||||
mount_state: WriteExpect<'a, UpdateTracker<MountState>>,
|
||||
is_mounts: WriteExpect<'a, UpdateTracker<Is<Mount>>>,
|
||||
is_riders: WriteExpect<'a, UpdateTracker<Is<Rider>>>,
|
||||
group: WriteExpect<'a, UpdateTracker<Group>>,
|
||||
mass: WriteExpect<'a, UpdateTracker<Mass>>,
|
||||
density: WriteExpect<'a, UpdateTracker<Density>>,
|
||||
@ -323,8 +325,8 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
||||
trackers.light_emitter.record_changes(&comps.light_emitter);
|
||||
trackers.item.record_changes(&comps.item);
|
||||
trackers.scale.record_changes(&comps.scale);
|
||||
trackers.mounting.record_changes(&comps.mounting);
|
||||
trackers.mount_state.record_changes(&comps.mount_state);
|
||||
trackers.is_mounts.record_changes(&comps.is_mount);
|
||||
trackers.is_riders.record_changes(&comps.is_rider);
|
||||
trackers.group.record_changes(&comps.group);
|
||||
trackers.mass.record_changes(&comps.mass);
|
||||
trackers.density.record_changes(&comps.density);
|
||||
@ -366,8 +368,8 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
||||
log_counts!(light_emitter, "Light emitters");
|
||||
log_counts!(item, "Items");
|
||||
log_counts!(scale, "Scales");
|
||||
log_counts!(mounting, "Mountings");
|
||||
log_counts!(mount_state, "Mount States");
|
||||
log_counts!(is_mounts, "mounts");
|
||||
log_counts!(is_riders, "riders");
|
||||
log_counts!(mass, "Masses");
|
||||
log_counts!(mass, "Densities");
|
||||
log_counts!(collider, "Colliders");
|
||||
@ -396,8 +398,8 @@ pub fn register_trackers(world: &mut World) {
|
||||
world.register_tracker::<LightEmitter>();
|
||||
world.register_tracker::<Item>();
|
||||
world.register_tracker::<Scale>();
|
||||
world.register_tracker::<Mounting>();
|
||||
world.register_tracker::<MountState>();
|
||||
world.register_tracker::<Is<Mount>>();
|
||||
world.register_tracker::<Is<Rider>>();
|
||||
world.register_tracker::<Group>();
|
||||
world.register_tracker::<Mass>();
|
||||
world.register_tracker::<Density>();
|
||||
|
@ -31,10 +31,11 @@ tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy", "
|
||||
tracy-memory = ["tracy"] # enables heap profiling with tracy
|
||||
plugins = ["client/plugins"]
|
||||
egui-ui = ["voxygen-egui", "egui", "egui_wgpu_backend", "egui_winit_platform"]
|
||||
shaderc-from-source = ["shaderc/build-from-source"]
|
||||
|
||||
# We don't ship egui with published release builds so a separate feature is required that excludes it.
|
||||
default-publish = ["singleplayer", "native-dialog", "plugins", "simd"]
|
||||
default = ["default-publish", "egui-ui", "hot-reloading"]
|
||||
default = ["default-publish", "egui-ui", "hot-reloading", "shaderc-from-source"]
|
||||
|
||||
[dependencies]
|
||||
client = {package = "veloren-client", path = "../client"}
|
||||
|
@ -132,7 +132,7 @@ impl Skeleton for BipedLargeSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::BipedLarge(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -73,7 +73,7 @@ impl Skeleton for BipedSmallSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::BipedSmall(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -101,7 +101,7 @@ impl Skeleton for BirdLargeSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::BirdLarge(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -55,7 +55,7 @@ impl Skeleton for BirdMediumSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::BirdMedium(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -149,7 +149,7 @@ impl Skeleton for CharacterSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::Humanoid(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -76,7 +76,7 @@ impl Skeleton for DragonSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::Dragon(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -55,7 +55,7 @@ impl Skeleton for FishMediumSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::FishMedium(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -46,7 +46,7 @@ impl Skeleton for FishSmallSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::FishSmall(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -78,7 +78,7 @@ impl Skeleton for GolemSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::Golem(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -44,7 +44,7 @@ impl Skeleton for ObjectSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::Object(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -74,7 +74,7 @@ impl Skeleton for QuadrupedLowSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::QuadrupedLow(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -60,7 +60,7 @@ impl Skeleton for QuadrupedSmallSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::QuadrupedSmall(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -47,7 +47,7 @@ impl Skeleton for ShipSkeleton {
|
||||
mount_bone: Transform {
|
||||
position: (base_mat * scale_mat).mul_point(
|
||||
common::comp::Body::Ship(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
),
|
||||
|
@ -79,7 +79,7 @@ impl Skeleton for TheropodSkeleton {
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::Theropod(body)
|
||||
.mountee_offset()
|
||||
.mount_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
..Default::default()
|
||||
|
@ -33,13 +33,15 @@ use common::{
|
||||
inventory::slot::EquipSlot,
|
||||
item::{Hands, ItemKind, ToolKind},
|
||||
Body, CharacterState, Collider, Controller, Health, Inventory, Item, Last, LightAnimation,
|
||||
LightEmitter, Mounting, Ori, PhysicsState, PoiseState, Pos, Scale, Vel,
|
||||
LightEmitter, Ori, PhysicsState, PoiseState, Pos, Scale, Vel,
|
||||
},
|
||||
resources::{DeltaTime, Time},
|
||||
states::{equipping, idle, utils::StageSection, wielding},
|
||||
terrain::TerrainChunk,
|
||||
uid::UidAllocator,
|
||||
vol::RectRasterableVol,
|
||||
link::Is,
|
||||
mounting::Rider,
|
||||
};
|
||||
use common_base::span;
|
||||
use common_state::State;
|
||||
@ -626,7 +628,7 @@ impl FigureMgr {
|
||||
inventory,
|
||||
item,
|
||||
light_emitter,
|
||||
mountings,
|
||||
is_rider,
|
||||
collider,
|
||||
),
|
||||
) in (
|
||||
@ -644,7 +646,7 @@ impl FigureMgr {
|
||||
ecs.read_storage::<Inventory>().maybe(),
|
||||
ecs.read_storage::<Item>().maybe(),
|
||||
ecs.read_storage::<LightEmitter>().maybe(),
|
||||
ecs.read_storage::<Mounting>().maybe(),
|
||||
ecs.read_storage::<Is<Rider>>().maybe(),
|
||||
ecs.read_storage::<Collider>().maybe(),
|
||||
)
|
||||
.join()
|
||||
@ -791,12 +793,12 @@ impl FigureMgr {
|
||||
|
||||
let hands = (active_tool_hand, second_tool_hand);
|
||||
|
||||
// If a mountee exists, get its animated mounting transform and its position
|
||||
// If a mount exists, get its animated mounting transform and its position
|
||||
let mount_transform_pos = (|| -> Option<_> {
|
||||
let Mounting(entity) = mountings?;
|
||||
let entity = uid_allocator.retrieve_entity_internal((*entity).into())?;
|
||||
let body = *bodies.get(entity)?;
|
||||
let meta = self.states.get_mut(&body, &entity)?;
|
||||
let mount = is_rider?.mount;
|
||||
let mount = uid_allocator.retrieve_entity_internal(mount.into())?;
|
||||
let body = *bodies.get(mount)?;
|
||||
let meta = self.states.get_mut(&body, &mount)?;
|
||||
Some((meta.mount_transform, meta.mount_world_pos))
|
||||
})();
|
||||
|
||||
@ -869,7 +871,7 @@ impl FigureMgr {
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
mountings.is_some(),
|
||||
is_rider.is_some(),
|
||||
) {
|
||||
// Standing
|
||||
(true, false, false, false) => {
|
||||
@ -5519,9 +5521,9 @@ impl FigureColLights {
|
||||
|
||||
pub struct FigureStateMeta {
|
||||
lantern_offset: Option<anim::vek::Vec3<f32>>,
|
||||
// Animation to be applied to mounter of this entity
|
||||
// Animation to be applied to rider of this entity
|
||||
mount_transform: anim::vek::Transform<f32, f32, f32>,
|
||||
// Contains the position of this figure or if it is a mounter it will contain the mountee's
|
||||
// Contains the position of this figure or if it is a rider it will contain the mount's
|
||||
// mount_world_pos
|
||||
// Unlike the interpolated position stored in the ecs this will be propagated along
|
||||
// mount chains
|
||||
@ -5635,7 +5637,7 @@ impl<S: Skeleton> FigureState<S> {
|
||||
model: Option<&FigureModelEntry<N>>,
|
||||
// TODO: there is the potential to drop the optional body from the common params and just
|
||||
// use this one but we need to add a function to the skelton trait or something in order to
|
||||
// get the mounter offset
|
||||
// get the rider offset
|
||||
skel_body: S::Body,
|
||||
) {
|
||||
// NOTE: As long as update() always gets called after get_or_create_model(), and
|
||||
@ -5672,26 +5674,26 @@ impl<S: Skeleton> FigureState<S> {
|
||||
let scale_mat = anim::vek::Mat4::scaling_3d(anim::vek::Vec3::from(*scale));
|
||||
if let Some((transform, _)) = *mount_transform_pos {
|
||||
// Note: if we had a way to compute a "default" transform of the bones then in
|
||||
// the animations we could make use of the mountee_offset from common by
|
||||
// computing what the offset of the mounter is from the mounted
|
||||
// bone in its default position when the mounter has the mount
|
||||
// the animations we could make use of the mount_offset from common by
|
||||
// computing what the offset of the rider is from the mounted
|
||||
// bone in its default position when the rider has the mount
|
||||
// offset in common applied to it. Since we don't have this
|
||||
// right now we instead need to recreate the same effect in the
|
||||
// animations and keep it in sync.
|
||||
//
|
||||
// Component of mounting offset specific to the mounter.
|
||||
let mounter_offset = anim::vek::Mat4::<f32>::translation_3d(
|
||||
body.map_or_else(Vec3::zero, |b| b.mounter_offset()),
|
||||
// Component of mounting offset specific to the rider.
|
||||
let rider_offset = anim::vek::Mat4::<f32>::translation_3d(
|
||||
body.map_or_else(Vec3::zero, |b| b.rider_offset()),
|
||||
);
|
||||
|
||||
// NOTE: It is kind of a hack to use this entity's ori here if it is
|
||||
// mounted on another but this happens to match the ori of the
|
||||
// mountee so it works, change this if it causes jankiness in the future.
|
||||
// mount so it works, change this if it causes jankiness in the future.
|
||||
let transform = anim::vek::Transform {
|
||||
orientation: *ori * transform.orientation,
|
||||
..transform
|
||||
};
|
||||
anim::vek::Mat4::from(transform) * mounter_offset * scale_mat
|
||||
anim::vek::Mat4::from(transform) * rider_offset * scale_mat
|
||||
} else {
|
||||
let ori_mat = anim::vek::Mat4::from(*ori);
|
||||
ori_mat * scale_mat
|
||||
|
@ -27,6 +27,8 @@ use common::{
|
||||
trade::TradeResult,
|
||||
util::{Dir, Plane},
|
||||
vol::ReadVol,
|
||||
link::Is,
|
||||
mounting::{Mounting, Mount},
|
||||
};
|
||||
use common_base::{prof_span, span};
|
||||
use common_net::{
|
||||
@ -670,7 +672,7 @@ impl PlayState for SessionState {
|
||||
},
|
||||
GameInput::Mount if state => {
|
||||
let mut client = self.client.borrow_mut();
|
||||
if client.is_mounted() {
|
||||
if client.is_riding() {
|
||||
client.unmount();
|
||||
} else {
|
||||
let player_pos = client
|
||||
@ -683,16 +685,11 @@ impl PlayState for SessionState {
|
||||
let closest_mountable_entity = (
|
||||
&client.state().ecs().entities(),
|
||||
&client.state().ecs().read_storage::<comp::Pos>(),
|
||||
&client
|
||||
.state()
|
||||
.ecs()
|
||||
.read_storage::<comp::MountState>(),
|
||||
// TODO: More cleverly filter by things that can actually be mounted
|
||||
!&client.state().ecs().read_storage::<Is<Mount>>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, mount_state)| {
|
||||
*entity != client.entity()
|
||||
&& **mount_state == comp::MountState::Unmounted
|
||||
})
|
||||
.filter(|(entity, _, mount_state)| *entity != client.entity())
|
||||
.map(|(entity, pos, _)| {
|
||||
(entity, player_pos.0.distance_squared(pos.0))
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user