veloren_common_systems/
mount.rs

1use common::{
2    combat::RiderEffects,
3    comp::{
4        Body, Buff, BuffCategory, BuffChange, Buffs, CharacterActivity, Collider, ControlAction,
5        Controller, InputKind, Mass, Ori, PhysicsState, Pos, Scale, Stats, Vel, buff::DestInfo,
6    },
7    event::{BuffEvent, EmitExt},
8    event_emitters,
9    link::Is,
10    mounting::{Mount, Rider, VolumeRider},
11    resources::Time,
12    terrain::TerrainGrid,
13    uid::IdMaps,
14};
15use common_ecs::{Job, Origin, Phase, System};
16use specs::{Entities, Join, LendJoin, Read, ReadExpect, ReadStorage, WriteStorage};
17use vek::*;
18
19event_emitters! {
20    struct Events[EventEmitters] {
21        buff: BuffEvent,
22    }
23}
24
25/// This system is responsible for controlling mounts
26#[derive(Default)]
27pub struct Sys;
28impl<'a> System<'a> for Sys {
29    type SystemData = (
30        Read<'a, IdMaps>,
31        Read<'a, Time>,
32        ReadExpect<'a, TerrainGrid>,
33        Events<'a>,
34        Entities<'a>,
35        WriteStorage<'a, Controller>,
36        ReadStorage<'a, Is<Rider>>,
37        ReadStorage<'a, Is<Mount>>,
38        ReadStorage<'a, Is<VolumeRider>>,
39        WriteStorage<'a, Pos>,
40        WriteStorage<'a, Vel>,
41        WriteStorage<'a, Ori>,
42        WriteStorage<'a, CharacterActivity>,
43        WriteStorage<'a, PhysicsState>,
44        ReadStorage<'a, Body>,
45        ReadStorage<'a, Scale>,
46        ReadStorage<'a, Collider>,
47        ReadStorage<'a, Buffs>,
48        ReadStorage<'a, Stats>,
49        ReadStorage<'a, Mass>,
50        ReadStorage<'a, RiderEffects>,
51    );
52
53    const NAME: &'static str = "mount";
54    const ORIGIN: Origin = Origin::Common;
55    const PHASE: Phase = Phase::Create;
56
57    fn run(
58        _job: &mut Job<Self>,
59        (
60            id_maps,
61            time,
62            terrain,
63            events,
64            entities,
65            mut controllers,
66            is_riders,
67            is_mounts,
68            is_volume_riders,
69            mut positions,
70            mut velocities,
71            mut orientations,
72            mut character_activities,
73            mut physics_states,
74            bodies,
75            scales,
76            colliders,
77            buffs,
78            stats,
79            masses,
80            rider_effects,
81        ): Self::SystemData,
82    ) {
83        let mut emitters = events.get_emitters();
84        // For each mount...
85        for (entity, is_mount, body, rider_effects) in
86            (&entities, &is_mounts, bodies.maybe(), rider_effects.maybe()).join()
87        {
88            let Some(rider_entity) = id_maps.uid_entity(is_mount.rider) else {
89                continue;
90            };
91
92            if let Some(rider_effects) = rider_effects
93                && let Some(target_buffs) = buffs.get(rider_entity)
94            {
95                for effect in rider_effects.0.iter() {
96                    let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
97                        buff.cat_ids.iter()
98                            .any(|cat_id| matches!(cat_id, BuffCategory::FromLink(link) if link.is_link(is_mount.get_link())))
99                            && buff.kind == effect.kind && buff.data.strength >= effect.data.strength
100                    });
101
102                    if emit_buff {
103                        let dest_info = DestInfo {
104                            stats: stats.get(rider_entity),
105                            mass: masses.get(rider_entity),
106                        };
107                        let mut cat_ids = effect.cat_ids.clone();
108                        cat_ids.push(BuffCategory::FromLink(
109                            is_mount.get_link().downgrade().into_dyn(),
110                        ));
111
112                        emitters.emit(BuffEvent {
113                            entity: rider_entity,
114                            buff_change: BuffChange::Add(Buff::new(
115                                effect.kind,
116                                effect.data,
117                                cat_ids,
118                                common::comp::BuffSource::Character { by: is_mount.mount },
119                                *time,
120                                dest_info,
121                                masses.get(entity),
122                            )),
123                        });
124                    }
125                }
126            }
127            // ...find the rider...
128            let Some(inputs_and_actions) = controllers.get_mut(rider_entity).map(|c| {
129                // Only take inputs and actions from the rider if the mount is not
130                // intelligent (TODO: expand the definition of 'intelligent').
131                if !matches!(body, Some(Body::Humanoid(_))) {
132                    let actions = c
133                        .actions
134                        .extract_if(.., |action| match action {
135                            ControlAction::StartInput { input: i, .. }
136                            | ControlAction::CancelInput(i) => {
137                                matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
138                            },
139                            _ => false,
140                        })
141                        .collect();
142                    Some((c.inputs.clone(), actions))
143                } else {
144                    None
145                }
146            }) else {
147                continue;
148            };
149
150            // ...apply the mount's position/ori/velocity to the rider...
151            let pos = positions.get(entity).copied();
152            let ori = orientations.get(entity).copied();
153            let vel = velocities.get(entity).copied();
154            if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
155                let mounter_body = bodies.get(rider_entity);
156                let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset)
157                    * scales.get(entity).map_or(1.0, |s| s.0)
158                    + mounter_body.map_or(Vec3::zero(), Body::rider_offset)
159                        * scales.get(rider_entity).map_or(1.0, |s| s.0);
160                let _ =
161                    positions.insert(rider_entity, Pos(pos.0 + ori.to_quat() * mounting_offset));
162                let _ = orientations.insert(rider_entity, ori);
163                let _ = velocities.insert(rider_entity, vel);
164            }
165            // ...and apply the rider's inputs to the mount's controller
166            if let Some((inputs, actions)) = inputs_and_actions
167                && let Some(controller) = controllers.get_mut(entity)
168            {
169                controller.inputs = inputs;
170                controller.actions = actions;
171            }
172        }
173
174        // Since physics state isn't updated while riding we set it to default.
175        // TODO: Could this be done only once when the link is first created? Has to
176        // happen on both server and client.
177        for (physics_state, _) in (
178            &mut physics_states,
179            is_riders.mask() | is_volume_riders.mask(),
180        )
181            .join()
182        {
183            *physics_state = PhysicsState::default();
184        }
185
186        // For each volume rider.
187        for (entity, is_volume_rider) in (&entities, &is_volume_riders).join() {
188            if let Some((mat, _)) = is_volume_rider.pos.get_mount_mat(
189                &terrain,
190                &id_maps,
191                |e| positions.get(e).copied().zip(orientations.get(e).copied()),
192                &colliders,
193            ) {
194                if let Some(pos) = positions.get_mut(entity) {
195                    pos.0 = mat.mul_point(Vec3::zero());
196                }
197                if let Some(ori) = orientations.get_mut(entity) {
198                    *ori = Ori::from_unnormalized_vec(mat.mul_direction(Vec3::unit_y()))
199                        .unwrap_or_default();
200                }
201            }
202            let v = match is_volume_rider.pos.kind {
203                common::mounting::Volume::Terrain => Vec3::zero(),
204                common::mounting::Volume::Entity(uid) => {
205                    if let Some(v) = id_maps.uid_entity(uid).and_then(|e| velocities.get(e)) {
206                        v.0
207                    } else {
208                        Vec3::zero()
209                    }
210                },
211            };
212            if let Some(vel) = velocities.get_mut(entity) {
213                vel.0 = v;
214            }
215
216            // Check if the volume has buffs if they do apply them to the rider via a
217            // BuffEvent
218
219            // TODO: This is code copy of the mounting effects. We can probably consolidate
220            // at some point.
221            if let Some(target_buffs) = buffs.get(entity)
222                && let Some(block_buffs) = is_volume_rider.block.mount_buffs()
223            {
224                for effect in block_buffs.iter() {
225                    let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
226                        buff.cat_ids.iter()
227                            .any(|cat_id| matches!(cat_id, BuffCategory::FromLink(link) if link.is_link(is_volume_rider.get_link())))
228                            && buff.kind == effect.kind && buff.data.strength >= effect.data.strength
229                    });
230
231                    if emit_buff {
232                        let dest_info = DestInfo {
233                            stats: stats.get(entity),
234                            mass: masses.get(entity),
235                        };
236                        let mut cat_ids = effect.cat_ids.clone();
237                        cat_ids.push(BuffCategory::FromLink(
238                            is_volume_rider.get_link().downgrade().into_dyn(),
239                        ));
240
241                        emitters.emit(BuffEvent {
242                            entity,
243                            buff_change: BuffChange::Add(Buff::new(
244                                effect.kind,
245                                effect.data,
246                                cat_ids,
247                                common::comp::BuffSource::Block,
248                                *time,
249                                dest_info,
250                                masses.get(entity),
251                            )),
252                        });
253                    }
254                }
255            }
256
257            let inputs = controllers.get_mut(entity).map(|c| {
258                let actions: Vec<_> = c
259                    .actions
260                    .extract_if(.., |action| match action {
261                        ControlAction::StartInput { input: i, .. }
262                        | ControlAction::CancelInput(i) => {
263                            matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
264                        },
265                        _ => false,
266                    })
267                    .collect();
268                let inputs = c.inputs.clone();
269
270                (actions, inputs)
271            });
272
273            if is_volume_rider.block.is_controller() {
274                if let Some((actions, inputs)) = inputs {
275                    if let Some(mut character_activity) = character_activities
276                        .get_mut(entity)
277                        .filter(|c| c.steer_dir != inputs.move_dir.y)
278                    {
279                        character_activity.steer_dir = inputs.move_dir.y;
280                    }
281                    match is_volume_rider.pos.kind {
282                        common::mounting::Volume::Entity(uid) => {
283                            if let Some(controller) =
284                                id_maps.uid_entity(uid).and_then(|e| controllers.get_mut(e))
285                            {
286                                controller.inputs = inputs;
287                                controller.actions = actions;
288                            }
289                        },
290                        common::mounting::Volume::Terrain => {},
291                    }
292                }
293            }
294        }
295    }
296}