mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
204 lines
8.0 KiB
Rust
204 lines
8.0 KiB
Rust
use common::{
|
|
comp::{
|
|
Body, CharacterActivity, Collider, ControlAction, Controller, InputKind, Ori, Pos, Scale,
|
|
Vel,
|
|
},
|
|
link::Is,
|
|
mounting::{Mount, VolumeRider},
|
|
terrain::TerrainGrid,
|
|
uid::IdMaps,
|
|
};
|
|
use common_ecs::{Job, Origin, Phase, System};
|
|
use specs::{Entities, Join, LendJoin, Read, ReadExpect, ReadStorage, WriteStorage};
|
|
use tracing::error;
|
|
use vek::*;
|
|
|
|
/// This system is responsible for controlling mounts
|
|
#[derive(Default)]
|
|
pub struct Sys;
|
|
impl<'a> System<'a> for Sys {
|
|
type SystemData = (
|
|
Read<'a, IdMaps>,
|
|
ReadExpect<'a, TerrainGrid>,
|
|
Entities<'a>,
|
|
WriteStorage<'a, Controller>,
|
|
ReadStorage<'a, Is<Mount>>,
|
|
ReadStorage<'a, Is<VolumeRider>>,
|
|
WriteStorage<'a, Pos>,
|
|
WriteStorage<'a, Vel>,
|
|
WriteStorage<'a, Ori>,
|
|
WriteStorage<'a, CharacterActivity>,
|
|
ReadStorage<'a, Body>,
|
|
ReadStorage<'a, Scale>,
|
|
ReadStorage<'a, Collider>,
|
|
);
|
|
|
|
const NAME: &'static str = "mount";
|
|
const ORIGIN: Origin = Origin::Common;
|
|
const PHASE: Phase = Phase::Create;
|
|
|
|
fn run(
|
|
_job: &mut Job<Self>,
|
|
(
|
|
id_maps,
|
|
terrain,
|
|
entities,
|
|
mut controllers,
|
|
is_mounts,
|
|
is_volume_riders,
|
|
mut positions,
|
|
mut velocities,
|
|
mut orientations,
|
|
mut character_activity,
|
|
bodies,
|
|
scales,
|
|
colliders,
|
|
): Self::SystemData,
|
|
) {
|
|
// For each mount...
|
|
for (entity, is_mount, body) in (&entities, &is_mounts, bodies.maybe()).join() {
|
|
// ...find the rider...
|
|
let Some((inputs_and_actions, rider)) =
|
|
id_maps.uid_entity(is_mount.rider).and_then(|rider| {
|
|
controllers.get_mut(rider).map(|c| {
|
|
(
|
|
// Only take inputs and actions from the rider if the mount is not
|
|
// intelligent (TODO: expand the definition of 'intelligent').
|
|
if !matches!(body, Some(Body::Humanoid(_))) {
|
|
let actions = c
|
|
.actions
|
|
.extract_if(|action| match action {
|
|
ControlAction::StartInput { input: i, .. }
|
|
| ControlAction::CancelInput(i) => matches!(
|
|
i,
|
|
InputKind::Jump | InputKind::Fly | InputKind::Roll
|
|
),
|
|
_ => false,
|
|
})
|
|
.collect();
|
|
Some((c.inputs.clone(), actions))
|
|
} else {
|
|
None
|
|
},
|
|
rider,
|
|
)
|
|
})
|
|
})
|
|
else {
|
|
continue;
|
|
};
|
|
|
|
// ...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)
|
|
* scales.get(entity).map_or(1.0, |s| s.0)
|
|
+ mounter_body.map_or(Vec3::zero(), Body::rider_offset)
|
|
* scales.get(rider).map_or(1.0, |s| s.0);
|
|
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((inputs, actions)) = inputs_and_actions
|
|
&& let Some(controller) = controllers.get_mut(entity)
|
|
{
|
|
controller.inputs = inputs;
|
|
controller.actions = actions;
|
|
}
|
|
}
|
|
|
|
// For each volume rider.
|
|
for (entity, is_volume_rider) in (&entities, &is_volume_riders).join() {
|
|
if let Some((mut mat, volume_ori, _)) = is_volume_rider.pos.get_block_and_transform(
|
|
&terrain,
|
|
&id_maps,
|
|
|e| positions.get(e).copied().zip(orientations.get(e).copied()),
|
|
&colliders,
|
|
) {
|
|
let Some((mount_offset, mount_dir)) = is_volume_rider.block.mount_offset() else {
|
|
error!("Mounted on unmountable block");
|
|
continue;
|
|
};
|
|
|
|
let mount_block_ori = if let Some(ori) = is_volume_rider.block.get_ori() {
|
|
mat *= Mat4::identity()
|
|
.translated_3d(mount_offset)
|
|
.rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
|
|
.translated_3d(Vec3::new(0.5, 0.5, 0.0));
|
|
ori
|
|
} else {
|
|
mat *= Mat4::identity().translated_3d(mount_offset + Vec3::new(0.5, 0.5, 0.0));
|
|
0
|
|
};
|
|
|
|
if let Some(pos) = positions.get_mut(entity) {
|
|
pos.0 = mat.mul_point(Vec3::zero());
|
|
}
|
|
if let Some(ori) = orientations.get_mut(entity) {
|
|
*ori = volume_ori.rotated(
|
|
Ori::from_unnormalized_vec(mount_dir)
|
|
.unwrap_or_default()
|
|
.to_quat()
|
|
.rotated_z(std::f32::consts::PI * 0.25 * mount_block_ori as f32),
|
|
);
|
|
}
|
|
}
|
|
let v = match is_volume_rider.pos.kind {
|
|
common::mounting::Volume::Terrain => Vec3::zero(),
|
|
common::mounting::Volume::Entity(uid) => {
|
|
if let Some(v) = id_maps.uid_entity(uid).and_then(|e| velocities.get(e)) {
|
|
v.0
|
|
} else {
|
|
Vec3::zero()
|
|
}
|
|
},
|
|
};
|
|
if let Some(vel) = velocities.get_mut(entity) {
|
|
vel.0 = v;
|
|
}
|
|
|
|
let inputs = controllers.get_mut(entity).map(|c| {
|
|
let actions: Vec<_> = c
|
|
.actions
|
|
.extract_if(|action| match action {
|
|
ControlAction::StartInput { input: i, .. }
|
|
| ControlAction::CancelInput(i) => {
|
|
matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
|
|
},
|
|
_ => false,
|
|
})
|
|
.collect();
|
|
let inputs = c.inputs.clone();
|
|
|
|
(actions, inputs)
|
|
});
|
|
|
|
if is_volume_rider.block.is_controller() {
|
|
if let Some((actions, inputs)) = inputs {
|
|
if let Some(mut character_activity) = character_activity
|
|
.get_mut(entity)
|
|
.filter(|c| c.steer_dir != inputs.move_dir.y)
|
|
{
|
|
character_activity.steer_dir = inputs.move_dir.y;
|
|
}
|
|
match is_volume_rider.pos.kind {
|
|
common::mounting::Volume::Entity(uid) => {
|
|
if let Some(controller) =
|
|
id_maps.uid_entity(uid).and_then(|e| controllers.get_mut(e))
|
|
{
|
|
controller.inputs = inputs;
|
|
controller.actions = actions;
|
|
}
|
|
},
|
|
common::mounting::Volume::Terrain => {},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|