diff --git a/client/src/lib.rs b/client/src/lib.rs index 8d5fb990c9..2d848d5fba 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -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::() + .read_storage::>() .get(self.entity()) .is_some() } diff --git a/common/net/src/msg/ecs_packet.rs b/common/net/src/msg/ecs_packet.rs index efaa3fb36a..0e5250487d 100644 --- a/common/net/src/msg/ecs_packet.rs +++ b/common/net/src/msg/ecs_packet.rs @@ -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), + IsRider(Is), Mass(comp::Mass), Density(comp::Density), Collider(comp::Collider), @@ -63,8 +68,8 @@ sum_type! { Item(PhantomData), Scale(PhantomData), Group(PhantomData), - MountState(PhantomData), - Mounting(PhantomData), + IsMount(PhantomData>), + IsRider(PhantomData>), Mass(PhantomData), Density(PhantomData), Collider(PhantomData), @@ -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::(entity, world), EcsCompPhantom::Scale(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Group(_) => sync::handle_remove::(entity, world), - EcsCompPhantom::MountState(_) => sync::handle_remove::(entity, world), - EcsCompPhantom::Mounting(_) => sync::handle_remove::(entity, world), + EcsCompPhantom::IsMount(_) => sync::handle_remove::>(entity, world), + EcsCompPhantom::IsRider(_) => sync::handle_remove::>(entity, world), EcsCompPhantom::Mass(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Density(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Collider(_) => sync::handle_remove::(entity, world), diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index dfb79752f9..096561bbbe 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -730,8 +730,8 @@ impl Body { ) } - /// Component of the mounting offset specific to the mountee - pub fn mountee_offset(&self) -> Vec3 { + /// Component of the mounting offset specific to the mount + pub fn mount_offset(&self) -> Vec3 { 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 { + /// Component of the mounting offset specific to the rider + pub fn rider_offset(&self) -> Vec3 { match self { Body::Humanoid(_) => [0.0, 0.0, 0.0], _ => [0.0, 0.0, 0.0], diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index fe803208ea..b5f15bd112 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -284,20 +284,3 @@ impl Controller { impl Component for Controller { type Storage = IdvStorage; } - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum MountState { - Unmounted, - MountedBy(Uid), -} - -impl Component for MountState { - type Storage = DerefFlaggedStorage>; -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Mounting(pub Uid); - -impl Component for Mounting { - type Storage = DerefFlaggedStorage>; -} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index b33dfbe589..18a8ac22dc 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -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, diff --git a/common/src/lib.rs b/common/src/lib.rs index e6109bb5c4..686385d78a 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -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; diff --git a/common/src/link.rs b/common/src/link.rs new file mode 100644 index 0000000000..7245e168f9 --- /dev/null +++ b/common/src/link.rs @@ -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, data: Self::CreateData<'_>) -> Result<(), ()>; + + type PersistData<'a>: SystemData<'a>; + fn persist(this: &LinkHandle, data: Self::PersistData<'_>) -> bool; + + type DeleteData<'a>: SystemData<'a>; + fn delete(this: &LinkHandle, data: Self::DeleteData<'_>); +} + +pub trait Role { + type Link: Link; +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Is { + #[serde(bound(serialize = "R::Link: Serialize"))] + #[serde(bound(deserialize = "R::Link: Deserialize<'de>"))] + link: LinkHandle, +} + +impl Is { + pub fn delete(&self, data: ::DeleteData<'_>) { + R::Link::delete(&self.link, data) + } +} + +impl Clone for Is { + fn clone(&self) -> Self { Self { link: self.link.clone() } } +} + +impl Deref for Is { + type Target = R::Link; + fn deref(&self) -> &Self::Target { &self.link } +} + +impl Component for Is + where R::Link: Send + Sync + 'static +{ + type Storage = DerefFlaggedStorage>; +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct LinkHandle { + link: Arc, +} + +impl Clone for LinkHandle { + fn clone(&self) -> Self { + Self { link: self.link.clone() } + } +} + +impl LinkHandle { + pub fn from_link(link: L) -> Self { + Self { link: Arc::new(link) } + } + + pub fn make_role>(&self) -> Is { + Is { link: self.clone() } + } +} + +impl Deref for LinkHandle { + type Target = L; + fn deref(&self) -> &Self::Target { &self.link } +} diff --git a/common/src/mounting.rs b/common/src/mounting.rs new file mode 100644 index 0000000000..0ba92800ed --- /dev/null +++ b/common/src/mounting.rs @@ -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>, + WriteStorage<'a, Is>, + ); + fn create(this: &LinkHandle, (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>, + ReadStorage<'a, Is>, + ); + fn persist(this: &LinkHandle, (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>, + WriteStorage<'a, Is>, + WriteStorage<'a, comp::Pos>, + WriteStorage<'a, comp::ForceUpdate>, + ReadExpect<'a, TerrainGrid>, + ); + fn delete(this: &LinkHandle, (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); + }); + } +} diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 2084852f56..d7812d4065 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -162,19 +162,21 @@ impl TerrainGrid { self.try_find_space(pos).unwrap_or(pos) } + pub fn is_space(&self, pos: Vec3) -> 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) -> Option> { 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)) } } diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 3a06dbc0a2..2812c2044e 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -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::(); ecs.register::(); ecs.register::(); - ecs.register::(); - ecs.register::(); + ecs.register::>(); + ecs.register::>(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index c5c5e86ad5..4c506a9b52 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -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>, 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; diff --git a/common/systems/src/lib.rs b/common/systems/src/lib.rs index 1a2de0b900..8a97a47771 100644 --- a/common/systems/src/lib.rs +++ b/common/systems/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(bool_to_option)] +#![feature(bool_to_option, let_else)] #![allow(clippy::option_map_unit_fn)] mod aura; diff --git a/common/systems/src/mount.rs b/common/systems/src/mount.rs index f2c8a69c98..7d574d9e3b 100644 --- a/common/systems/src/mount.rs +++ b/common/systems/src/mount.rs @@ -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>, + ReadStorage<'a, Is>, 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); } } } diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 42ebfc587b..0286b377f4 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -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>, 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; diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 049569d062..2274a4c017 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -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( diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index d3d92106e9..c0e6a7b0bf 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -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::() - .get(mounter) + .read_storage::>() + .get(rider) .is_none() { - let not_mounting_yet = matches!( - state.ecs().read_storage::().get(mountee), - Some(comp::MountState::Unmounted) - ); + let not_mounting_yet = state + .ecs() + .read_storage::>() + .get(mount) + .is_none(); let within_range = || { let positions = state.ecs().read_storage::(); - within_mounting_range(positions.get(mounter), positions.get(mountee)) + within_mounting_range(positions.get(rider), positions.get(mount)) }; let healths = state.ecs().read_storage::(); 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::(); - 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::() - .get(mounter) - .and_then(|mountee| state.ecs().entity_from_uid(mountee.0.into())); - if let Some(mountee_entity) = mountee_entity { - state - .ecs() - .write_storage::() - .get_mut(mountee_entity) - .map(|mut ms| *ms = comp::MountState::Unmounted); - } - state.delete_component::(mounter); + .write_storage::>() + .remove(rider); } /// FIXME: This code is dangerous and needs to be refactored. We can't just diff --git a/server/src/lib.rs b/server/src/lib.rs index fa19904156..fd2ef81fd6 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -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()); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 3aa1aa030e..b3a49e4e4e 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -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(&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(&mut self, link: L) -> Result<(), ()> { + let linker = LinkHandle::from_link(link); + + L::create( + &linker, + self.ecs().system_data(), + )?; + + self.ecs_mut() + .entry::>>() + .or_insert_with(Vec::new) + .push(linker); + + Ok(()) + } + + fn maintain_links(&mut self) { + fn maintain_link(state: &State) { + if let Some(mut handles) = state.ecs().try_fetch_mut::>>() { + handles.retain(|link| if L::persist(link, state.ecs().system_data()) { + true + } else { + L::delete(link, state.ecs().system_data()); + false + }); + } + } + + maintain_link::(self); + } + fn delete_entity_recorded( &mut self, entity: EcsEntity, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 171b1b44bf..4d3da59403 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -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>, 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"); diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 74f77376d0..8a27038121 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -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>, + pub is_rider: ReadStorage<'a, Is>, 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>, pub item: ReadExpect<'a, UpdateTracker>, pub scale: ReadExpect<'a, UpdateTracker>, - pub mounting: ReadExpect<'a, UpdateTracker>, - pub mount_state: ReadExpect<'a, UpdateTracker>, + pub is_mount: ReadExpect<'a, UpdateTracker>>, + pub is_rider: ReadExpect<'a, UpdateTracker>>, pub group: ReadExpect<'a, UpdateTracker>, pub mass: ReadExpect<'a, UpdateTracker>, pub density: ReadExpect<'a, UpdateTracker>, @@ -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>, item: WriteExpect<'a, UpdateTracker>, scale: WriteExpect<'a, UpdateTracker>, - mounting: WriteExpect<'a, UpdateTracker>, - mount_state: WriteExpect<'a, UpdateTracker>, + is_mounts: WriteExpect<'a, UpdateTracker>>, + is_riders: WriteExpect<'a, UpdateTracker>>, group: WriteExpect<'a, UpdateTracker>, mass: WriteExpect<'a, UpdateTracker>, density: WriteExpect<'a, UpdateTracker>, @@ -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::(); world.register_tracker::(); world.register_tracker::(); - world.register_tracker::(); - world.register_tracker::(); + world.register_tracker::>(); + world.register_tracker::>(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 84ac7ceee4..becf603e3b 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -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"} diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index e712e3c272..76f7a3cefb 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -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() diff --git a/voxygen/anim/src/biped_small/mod.rs b/voxygen/anim/src/biped_small/mod.rs index 2c533da2d6..e827241a30 100644 --- a/voxygen/anim/src/biped_small/mod.rs +++ b/voxygen/anim/src/biped_small/mod.rs @@ -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() diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 31569d6fd8..fd6b6198f0 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -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() diff --git a/voxygen/anim/src/bird_medium/mod.rs b/voxygen/anim/src/bird_medium/mod.rs index cb7a995b9b..dab98a6819 100644 --- a/voxygen/anim/src/bird_medium/mod.rs +++ b/voxygen/anim/src/bird_medium/mod.rs @@ -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() diff --git a/voxygen/anim/src/character/mod.rs b/voxygen/anim/src/character/mod.rs index d768e22488..d176eb326c 100644 --- a/voxygen/anim/src/character/mod.rs +++ b/voxygen/anim/src/character/mod.rs @@ -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() diff --git a/voxygen/anim/src/dragon/mod.rs b/voxygen/anim/src/dragon/mod.rs index 135ad304ac..7b82a7cc6a 100644 --- a/voxygen/anim/src/dragon/mod.rs +++ b/voxygen/anim/src/dragon/mod.rs @@ -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() diff --git a/voxygen/anim/src/fish_medium/mod.rs b/voxygen/anim/src/fish_medium/mod.rs index 9675f3322c..80685b7ea3 100644 --- a/voxygen/anim/src/fish_medium/mod.rs +++ b/voxygen/anim/src/fish_medium/mod.rs @@ -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() diff --git a/voxygen/anim/src/fish_small/mod.rs b/voxygen/anim/src/fish_small/mod.rs index 8935974732..a24dca196c 100644 --- a/voxygen/anim/src/fish_small/mod.rs +++ b/voxygen/anim/src/fish_small/mod.rs @@ -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() diff --git a/voxygen/anim/src/golem/mod.rs b/voxygen/anim/src/golem/mod.rs index 1b3cc3bc97..e32f1360eb 100644 --- a/voxygen/anim/src/golem/mod.rs +++ b/voxygen/anim/src/golem/mod.rs @@ -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() diff --git a/voxygen/anim/src/object/mod.rs b/voxygen/anim/src/object/mod.rs index 2125edd791..7d4cbbb2aa 100644 --- a/voxygen/anim/src/object/mod.rs +++ b/voxygen/anim/src/object/mod.rs @@ -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() diff --git a/voxygen/anim/src/quadruped_low/mod.rs b/voxygen/anim/src/quadruped_low/mod.rs index 46d32dc596..e96b348965 100644 --- a/voxygen/anim/src/quadruped_low/mod.rs +++ b/voxygen/anim/src/quadruped_low/mod.rs @@ -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() diff --git a/voxygen/anim/src/quadruped_small/mod.rs b/voxygen/anim/src/quadruped_small/mod.rs index 578dcb955d..dea71f5439 100644 --- a/voxygen/anim/src/quadruped_small/mod.rs +++ b/voxygen/anim/src/quadruped_small/mod.rs @@ -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() diff --git a/voxygen/anim/src/ship/mod.rs b/voxygen/anim/src/ship/mod.rs index 81d09e9a90..9c02b67178 100644 --- a/voxygen/anim/src/ship/mod.rs +++ b/voxygen/anim/src/ship/mod.rs @@ -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(), ), diff --git a/voxygen/anim/src/theropod/mod.rs b/voxygen/anim/src/theropod/mod.rs index b76b0652e9..21db915f25 100644 --- a/voxygen/anim/src/theropod/mod.rs +++ b/voxygen/anim/src/theropod/mod.rs @@ -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() diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 7208365b6c..90797c5880 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -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::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), + ecs.read_storage::>().maybe(), ecs.read_storage::().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>, - // Animation to be applied to mounter of this entity + // Animation to be applied to rider of this entity mount_transform: anim::vek::Transform, - // 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 FigureState { model: Option<&FigureModelEntry>, // 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 FigureState { 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::::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::::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 diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 359a90e01c..a329f66e1d 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -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::(), - &client - .state() - .ecs() - .read_storage::(), + // TODO: More cleverly filter by things that can actually be mounted + !&client.state().ecs().read_storage::>(), ) .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)) })