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::*;
#[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 (entity, is_mount, body) in (&entities, &is_mounts, bodies.maybe()).join() {
let Some((inputs_and_actions, rider)) =
id_maps.uid_entity(is_mount.rider).and_then(|rider| {
controllers.get_mut(rider).map(|c| {
(
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;
};
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);
}
if let Some((inputs, actions)) = inputs_and_actions
&& let Some(controller) = controllers.get_mut(entity)
{
controller.inputs = inputs;
controller.actions = actions;
}
}
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 => {},
}
}
}
}
}
}