Skip to main content

veloren_common_systems/
mount.rs

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