veloren/common/src/mounting.rs

414 lines
13 KiB
Rust
Raw Normal View History

use crate::{
2023-04-17 13:38:41 +00:00
comp::{self, ship::figuredata::VOXEL_COLLIDER_MANIFEST},
link::{Is, Link, LinkHandle, Role},
2023-04-17 13:38:41 +00:00
terrain::{Block, TerrainGrid},
uid::{Uid, UidAllocator},
2023-04-17 13:38:41 +00:00
vol::ReadVol,
};
2023-04-17 13:38:41 +00:00
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};
2023-04-17 13:38:41 +00:00
use specs::{
2023-04-23 15:45:28 +00:00
saveload::MarkerAllocator, storage::GenericWriteStorage, Component, DenseVecStorage, Entities,
Entity, Read, ReadExpect, ReadStorage, Write, WriteStorage,
2023-04-17 13:38:41 +00:00
};
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,
}
2023-01-14 22:47:05 +00:00
#[derive(Debug)]
pub enum MountingError {
NoSuchEntity,
NotMountable,
}
impl Link for Mounting {
type CreateData<'a> = (
Read<'a, UidAllocator>,
WriteStorage<'a, Is<Mount>>,
WriteStorage<'a, Is<Rider>>,
2023-04-17 13:38:41 +00:00
ReadStorage<'a, Is<VolumeRider>>,
);
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>,
);
type Error = MountingError;
type PersistData<'a> = (
Read<'a, UidAllocator>,
Entities<'a>,
ReadStorage<'a, comp::Health>,
ReadStorage<'a, Is<Mount>>,
ReadStorage<'a, Is<Rider>>,
);
fn create(
this: &LinkHandle<Self>,
2023-04-17 13:38:41 +00:00
(uid_allocator, mut is_mounts, mut is_riders, is_volume_rider): Self::CreateData<'_>,
) -> Result<(), Self::Error> {
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
2022-01-15 20:15:38 +00:00
if this.mount == this.rider {
// Forbid self-mounting
Err(MountingError::NotMountable)
2022-01-15 20:15:38 +00:00
} else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
2023-04-17 13:38:41 +00:00
let can_mount_with = |entity| {
!is_mounts.contains(entity)
&& !is_riders.contains(entity)
&& !is_volume_rider.contains(entity)
};
2023-01-14 22:47:05 +00:00
// Ensure that neither mount or rider are already part of a mounting
// relationship
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(MountingError::NotMountable)
}
} else {
Err(MountingError::NoSuchEntity)
}
}
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
}
}
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_ground(old_pos).map(|e| e as f32))
+ Vec3::new(0.5, 0.5, 0.0);
2022-07-31 21:01:10 +00:00
if let Some(force_update) = force_update.get_mut(rider) {
force_update.update();
}
});
}
}
2023-04-17 13:38:41 +00:00
#[derive(Serialize, Deserialize, Debug)]
pub struct VolumeRider;
impl Role for VolumeRider {
type Link = VolumeMounting;
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Volume {
Terrain,
Entity(Uid),
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct VolumePos {
pub kind: Volume,
pub pos: Vec3<i32>,
}
impl VolumePos {
pub fn terrain(block_pos: Vec3<i32>) -> Self {
Self {
kind: Volume::Terrain,
pos: block_pos,
}
}
pub fn entity(block_pos: Vec3<i32>, uid: Uid) -> Self {
Self {
kind: Volume::Entity(uid),
pos: block_pos,
}
}
}
impl VolumePos {
2023-05-06 10:31:54 +00:00
/// Retrieves the block and matrix transformation for this `VolumeBlock`
///
/// The transform is located in the blocks minimum position relative to the
/// volume.
2023-04-17 13:38:41 +00:00
pub fn get_block_and_transform(
&self,
terrain: &TerrainGrid,
uid_allocator: &UidAllocator,
2023-04-23 15:45:28 +00:00
mut read_pos_and_ori: impl FnMut(Entity) -> Option<(comp::Pos, comp::Ori)>,
2023-04-17 13:38:41 +00:00
colliders: &ReadStorage<comp::Collider>,
) -> Option<(Mat4<f32>, Block)> {
match self.kind {
Volume::Terrain => Some((
Mat4::translation_3d(self.pos.as_()),
*terrain.get(self.pos).ok()?,
)),
Volume::Entity(uid) => {
uid_allocator
.retrieve_entity_internal(uid.0)
.and_then(|entity| {
let collider = colliders.get(entity)?;
2023-04-23 15:45:28 +00:00
let (pos, ori) = read_pos_and_ori(entity)?;
2023-04-17 13:38:41 +00:00
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
let block = *voxel_collider.volume().get(self.pos).ok()?;
let local_translation = voxel_collider.translation + self.pos.as_();
let trans = Mat4::from(ori.to_quat()).translated_3d(pos.0)
* Mat4::<f32>::translation_3d(local_translation);
Some((trans, block))
})
},
}
}
2023-05-06 10:31:54 +00:00
/// Get the block at this `VolumePos`.
2023-04-17 13:38:41 +00:00
pub fn get_block(
&self,
terrain: &TerrainGrid,
uid_allocator: &UidAllocator,
colliders: &ReadStorage<comp::Collider>,
) -> Option<Block> {
match self.kind {
Volume::Terrain => Some(*terrain.get(self.pos).ok()?),
Volume::Entity(uid) => {
uid_allocator
.retrieve_entity_internal(uid.0)
.and_then(|entity| {
let collider = colliders.get(entity)?;
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
let block = *voxel_collider.volume().get(self.pos).ok()?;
Some(block)
})
},
}
}
}
#[derive(Default)]
pub struct VolumeRiders {
riders: HashSet<Vec3<i32>>,
}
impl Component for VolumeRiders {
type Storage = DenseVecStorage<Self>;
}
#[derive(Serialize, Deserialize, Debug)]
pub struct VolumeMounting {
pub pos: VolumePos,
pub block: Block,
pub rider: Uid,
}
impl Link for VolumeMounting {
type CreateData<'a> = (
Write<'a, VolumeRiders>,
WriteStorage<'a, VolumeRiders>,
WriteStorage<'a, Is<VolumeRider>>,
ReadStorage<'a, Is<Rider>>,
ReadStorage<'a, Is<Mount>>,
ReadExpect<'a, TerrainGrid>,
Read<'a, UidAllocator>,
ReadStorage<'a, comp::Collider>,
);
type DeleteData<'a> = (
Write<'a, VolumeRiders>,
WriteStorage<'a, VolumeRiders>,
WriteStorage<'a, Is<VolumeRider>>,
Read<'a, UidAllocator>,
);
type Error = MountingError;
type PersistData<'a> = (
Entities<'a>,
ReadStorage<'a, comp::Health>,
Read<'a, VolumeRiders>,
ReadStorage<'a, VolumeRiders>,
ReadStorage<'a, Is<VolumeRider>>,
ReadExpect<'a, TerrainGrid>,
Read<'a, UidAllocator>,
ReadStorage<'a, comp::Collider>,
);
fn create(
this: &LinkHandle<Self>,
(
mut terrain_riders,
mut volume_riders,
mut is_volume_riders,
is_riders,
is_mounts,
terrain_grid,
uid_allocator,
colliders,
): Self::CreateData<'_>,
) -> Result<(), Self::Error> {
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
let riders = match this.pos.kind {
Volume::Terrain => &mut *terrain_riders,
Volume::Entity(uid) => entity(uid)
.and_then(|entity| volume_riders.get_mut_or_default(entity))
.ok_or(MountingError::NoSuchEntity)?,
};
let rider = entity(this.rider).ok_or(MountingError::NoSuchEntity)?;
if !riders.riders.contains(&this.pos.pos)
&& !is_volume_riders.contains(rider)
&& !is_volume_riders.contains(rider)
&& !is_riders.contains(rider)
&& !is_mounts.contains(rider)
{
let block = this
.pos
.get_block(&terrain_grid, &uid_allocator, &colliders)
.ok_or(MountingError::NoSuchEntity)?;
if block == this.block {
let _ = is_volume_riders.insert(rider, this.make_role());
riders.riders.insert(this.pos.pos);
Ok(())
} else {
Err(MountingError::NotMountable)
}
} else {
Err(MountingError::NotMountable)
}
}
fn persist(
this: &LinkHandle<Self>,
(
entities,
healths,
terrain_riders,
volume_riders,
is_volume_riders,
terrain_grid,
uid_allocator,
colliders,
): Self::PersistData<'_>,
) -> bool {
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
let is_alive =
|entity| entities.is_alive(entity) && healths.get(entity).map_or(true, |h| !h.is_dead);
let riders = match this.pos.kind {
Volume::Terrain => &*terrain_riders,
Volume::Entity(uid) => {
let Some(riders) = entity(uid)
.filter(|entity| is_alive(*entity))
.and_then(|entity| volume_riders.get(entity)) else {
return false;
};
riders
},
};
let rider_exists = entity(this.rider).map_or(false, |rider| {
is_volume_riders.contains(rider) && is_alive(rider)
});
let mount_spot_exists = riders.riders.contains(&this.pos.pos);
let block_exists = this
.pos
.get_block(&terrain_grid, &uid_allocator, &colliders)
.map_or(false, |block| block == this.block);
rider_exists && mount_spot_exists && block_exists
}
fn delete(
this: &LinkHandle<Self>,
(mut terrain_riders, mut volume_riders, mut is_rider, uid_allocator): Self::DeleteData<'_>,
) {
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
let riders = match this.pos.kind {
Volume::Terrain => Some(&mut *terrain_riders),
Volume::Entity(uid) => {
entity(uid).and_then(|entity| volume_riders.get_mut_or_default(entity))
},
};
if let Some(riders) = riders {
riders.riders.remove(&this.pos.pos);
}
if let Some(entity) = entity(this.rider) {
is_rider.remove(entity);
}
}
}