veloren_common_systems/
mount.rs

1use common::{
2    comp::{
3        Body, CharacterActivity, Collider, ControlAction, Controller, InputKind, Ori, PhysicsState,
4        Pos, Scale, Vel,
5    },
6    link::Is,
7    mounting::{Mount, Rider, VolumeRider},
8    terrain::TerrainGrid,
9    uid::IdMaps,
10};
11use common_ecs::{Job, Origin, Phase, System};
12use specs::{Entities, Join, LendJoin, Read, ReadExpect, ReadStorage, WriteStorage};
13use vek::*;
14
15/// This system is responsible for controlling mounts
16#[derive(Default)]
17pub struct Sys;
18impl<'a> System<'a> for Sys {
19    type SystemData = (
20        Read<'a, IdMaps>,
21        ReadExpect<'a, TerrainGrid>,
22        Entities<'a>,
23        WriteStorage<'a, Controller>,
24        ReadStorage<'a, Is<Rider>>,
25        ReadStorage<'a, Is<Mount>>,
26        ReadStorage<'a, Is<VolumeRider>>,
27        WriteStorage<'a, Pos>,
28        WriteStorage<'a, Vel>,
29        WriteStorage<'a, Ori>,
30        WriteStorage<'a, CharacterActivity>,
31        WriteStorage<'a, PhysicsState>,
32        ReadStorage<'a, Body>,
33        ReadStorage<'a, Scale>,
34        ReadStorage<'a, Collider>,
35    );
36
37    const NAME: &'static str = "mount";
38    const ORIGIN: Origin = Origin::Common;
39    const PHASE: Phase = Phase::Create;
40
41    fn run(
42        _job: &mut Job<Self>,
43        (
44            id_maps,
45            terrain,
46            entities,
47            mut controllers,
48            is_riders,
49            is_mounts,
50            is_volume_riders,
51            mut positions,
52            mut velocities,
53            mut orientations,
54            mut character_activities,
55            mut physics_states,
56            bodies,
57            scales,
58            colliders,
59        ): Self::SystemData,
60    ) {
61        // For each mount...
62        for (entity, is_mount, body) in (&entities, &is_mounts, bodies.maybe()).join() {
63            // ...find the rider...
64            let Some((inputs_and_actions, rider)) =
65                id_maps.uid_entity(is_mount.rider).and_then(|rider| {
66                    controllers.get_mut(rider).map(|c| {
67                        (
68                            // Only take inputs and actions from the rider if the mount is not
69                            // intelligent (TODO: expand the definition of 'intelligent').
70                            if !matches!(body, Some(Body::Humanoid(_))) {
71                                let actions = c
72                                    .actions
73                                    .extract_if(.., |action| match action {
74                                        ControlAction::StartInput { input: i, .. }
75                                        | ControlAction::CancelInput(i) => matches!(
76                                            i,
77                                            InputKind::Jump | InputKind::Fly | InputKind::Roll
78                                        ),
79                                        _ => false,
80                                    })
81                                    .collect();
82                                Some((c.inputs.clone(), actions))
83                            } else {
84                                None
85                            },
86                            rider,
87                        )
88                    })
89                })
90            else {
91                continue;
92            };
93
94            // ...apply the mount's position/ori/velocity to the rider...
95            let pos = positions.get(entity).copied();
96            let ori = orientations.get(entity).copied();
97            let vel = velocities.get(entity).copied();
98            if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
99                let mounter_body = bodies.get(rider);
100                let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset)
101                    * scales.get(entity).map_or(1.0, |s| s.0)
102                    + mounter_body.map_or(Vec3::zero(), Body::rider_offset)
103                        * scales.get(rider).map_or(1.0, |s| s.0);
104                let _ = positions.insert(rider, Pos(pos.0 + ori.to_quat() * mounting_offset));
105                let _ = orientations.insert(rider, ori);
106                let _ = velocities.insert(rider, vel);
107            }
108            // ...and apply the rider's inputs to the mount's controller
109            if let Some((inputs, actions)) = inputs_and_actions
110                && let Some(controller) = controllers.get_mut(entity)
111            {
112                controller.inputs = inputs;
113                controller.actions = actions;
114            }
115        }
116
117        // Since physics state isn't updated while riding we set it to default.
118        // TODO: Could this be done only once when the link is first created? Has to
119        // happen on both server and client.
120        for (physics_state, _) in (
121            &mut physics_states,
122            is_riders.mask() | is_volume_riders.mask(),
123        )
124            .join()
125        {
126            *physics_state = PhysicsState::default();
127        }
128
129        // For each volume rider.
130        for (entity, is_volume_rider) in (&entities, &is_volume_riders).join() {
131            if let Some((mat, _)) = is_volume_rider.pos.get_mount_mat(
132                &terrain,
133                &id_maps,
134                |e| positions.get(e).copied().zip(orientations.get(e).copied()),
135                &colliders,
136            ) {
137                if let Some(pos) = positions.get_mut(entity) {
138                    pos.0 = mat.mul_point(Vec3::zero());
139                }
140                if let Some(ori) = orientations.get_mut(entity) {
141                    *ori = Ori::from_unnormalized_vec(mat.mul_direction(Vec3::unit_y()))
142                        .unwrap_or_default();
143                }
144            }
145            let v = match is_volume_rider.pos.kind {
146                common::mounting::Volume::Terrain => Vec3::zero(),
147                common::mounting::Volume::Entity(uid) => {
148                    if let Some(v) = id_maps.uid_entity(uid).and_then(|e| velocities.get(e)) {
149                        v.0
150                    } else {
151                        Vec3::zero()
152                    }
153                },
154            };
155            if let Some(vel) = velocities.get_mut(entity) {
156                vel.0 = v;
157            }
158
159            let inputs = controllers.get_mut(entity).map(|c| {
160                let actions: Vec<_> = c
161                    .actions
162                    .extract_if(.., |action| match action {
163                        ControlAction::StartInput { input: i, .. }
164                        | ControlAction::CancelInput(i) => {
165                            matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
166                        },
167                        _ => false,
168                    })
169                    .collect();
170                let inputs = c.inputs.clone();
171
172                (actions, inputs)
173            });
174
175            if is_volume_rider.block.is_controller() {
176                if let Some((actions, inputs)) = inputs {
177                    if let Some(mut character_activity) = character_activities
178                        .get_mut(entity)
179                        .filter(|c| c.steer_dir != inputs.move_dir.y)
180                    {
181                        character_activity.steer_dir = inputs.move_dir.y;
182                    }
183                    match is_volume_rider.pos.kind {
184                        common::mounting::Volume::Entity(uid) => {
185                            if let Some(controller) =
186                                id_maps.uid_entity(uid).and_then(|e| controllers.get_mut(e))
187                            {
188                                controller.inputs = inputs;
189                                controller.actions = actions;
190                            }
191                        },
192                        common::mounting::Volume::Terrain => {},
193                    }
194                }
195            }
196        }
197    }
198}