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