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            // Rider effects from mount.
93            if let Some(rider_effects) = rider_effects
94                && let Some(target_buffs) = buffs.get(rider_entity)
95            {
96                for effect in rider_effects.0.iter() {
97                    let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
98                        buff.cat_ids.iter()
99                            .any(|cat_id| matches!(cat_id, BuffCategory::FromLink(link) if link.is_link(is_mount.get_link())))
100                            && buff.kind == effect.kind && buff.data.strength >= effect.data.strength
101                    });
102
103                    if emit_buff {
104                        let dest_info = DestInfo {
105                            stats: stats.get(rider_entity),
106                            mass: masses.get(rider_entity),
107                        };
108                        let mut cat_ids = effect.cat_ids.clone();
109                        cat_ids.push(BuffCategory::FromLink(
110                            is_mount.get_link().downgrade().into_dyn(),
111                        ));
112
113                        emitters.emit(BuffEvent {
114                            entity: rider_entity,
115                            buff_change: BuffChange::Add(Buff::new(
116                                effect.kind,
117                                effect.data,
118                                cat_ids,
119                                common::comp::BuffSource::Character { by: is_mount.mount },
120                                *time,
121                                dest_info,
122                                masses.get(entity),
123                            )),
124                        });
125                    }
126                }
127            }
128            // ...find the rider...
129            let Some(inputs_and_actions) = controllers.get_mut(rider_entity).map(|c| {
130                // Only take inputs and actions from the rider if the mount is not
131                // intelligent (TODO: expand the definition of 'intelligent').
132                if body.is_some_and(|b| !b.has_free_will()) {
133                    let actions = c
134                        .actions
135                        .extract_if(.., |action| match action {
136                            ControlAction::StartInput { input: i, .. }
137                            | ControlAction::CancelInput { input: i } => {
138                                matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
139                            },
140                            _ => false,
141                        })
142                        .collect();
143                    Some((c.inputs.clone(), actions))
144                } else {
145                    None
146                }
147            }) else {
148                continue;
149            };
150
151            // ...apply the mount's position/ori/velocity to the rider...
152            let pos = positions.get(entity).copied();
153            let ori = orientations.get(entity).copied();
154            let vel = velocities.get(entity).copied();
155            if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
156                let mounter_body = bodies.get(rider_entity);
157                let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset)
158                    * scales.get(entity).map_or(1.0, |s| s.0)
159                    + mounter_body.map_or(Vec3::zero(), Body::rider_offset)
160                        * scales.get(rider_entity).map_or(1.0, |s| s.0);
161                let _ =
162                    positions.insert(rider_entity, Pos(pos.0 + ori.to_quat() * mounting_offset));
163                let _ = orientations.insert(rider_entity, ori);
164                let _ = velocities.insert(rider_entity, vel);
165            }
166            // ...and apply the rider's inputs to the mount's controller
167            if let Some((inputs, actions)) = inputs_and_actions
168                && let Some(controller) = controllers.get_mut(entity)
169            {
170                controller.inputs = inputs;
171                controller.actions = actions;
172            }
173        }
174
175        // Since physics state isn't updated while riding we set it to default.
176        // TODO: Could this be done only once when the link is first created? Has to
177        // happen on both server and client.
178        for (physics_state, _) in (
179            &mut physics_states,
180            is_riders.mask() | is_volume_riders.mask(),
181        )
182            .join()
183        {
184            *physics_state = PhysicsState::default();
185        }
186
187        // For each volume rider.
188        for (entity, is_volume_rider) in (&entities, &is_volume_riders).join() {
189            if let Some((mat, _)) = is_volume_rider.pos.get_mount_mat(
190                &terrain,
191                &id_maps,
192                |e| positions.get(e).copied().zip(orientations.get(e).copied()),
193                &colliders,
194            ) {
195                if let Some(pos) = positions.get_mut(entity) {
196                    pos.0 = mat.mul_point(Vec3::zero());
197                }
198                if let Some(ori) = orientations.get_mut(entity) {
199                    *ori = Ori::from_unnormalized_vec(mat.mul_direction(Vec3::unit_y()))
200                        .unwrap_or_default();
201                }
202            }
203            let v = match is_volume_rider.pos.kind {
204                common::mounting::Volume::Terrain => Vec3::zero(),
205                common::mounting::Volume::Entity(uid) => {
206                    if let Some(v) = id_maps.uid_entity(uid).and_then(|e| velocities.get(e)) {
207                        v.0
208                    } else {
209                        Vec3::zero()
210                    }
211                },
212            };
213            if let Some(vel) = velocities.get_mut(entity) {
214                vel.0 = v;
215            }
216
217            // Check if the volume has buffs if they do apply them to the rider via a
218            // BuffEvent
219
220            // TODO: This is code copy of the mounting effects. We can probably consolidate
221            // at some point.
222            if let Some(target_buffs) = buffs.get(entity)
223                && let Some(block_buffs) = is_volume_rider.block.mount_buffs()
224            {
225                for effect in block_buffs.iter() {
226                    let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
227                        buff.cat_ids.iter()
228                            .any(|cat_id| matches!(cat_id, BuffCategory::FromLink(link) if link.is_link(is_volume_rider.get_link())))
229                            && buff.kind == effect.kind && buff.data.strength >= effect.data.strength
230                    });
231
232                    if emit_buff {
233                        let dest_info = DestInfo {
234                            stats: stats.get(entity),
235                            mass: masses.get(entity),
236                        };
237                        let mut cat_ids = effect.cat_ids.clone();
238                        cat_ids.push(BuffCategory::FromLink(
239                            is_volume_rider.get_link().downgrade().into_dyn(),
240                        ));
241
242                        emitters.emit(BuffEvent {
243                            entity,
244                            buff_change: BuffChange::Add(Buff::new(
245                                effect.kind,
246                                effect.data,
247                                cat_ids,
248                                common::comp::BuffSource::Block,
249                                *time,
250                                dest_info,
251                                masses.get(entity),
252                            )),
253                        });
254                    }
255                }
256            }
257
258            let inputs = controllers.get_mut(entity).map(|c| {
259                let actions: Vec<_> = c
260                    .actions
261                    .extract_if(.., |action| match action {
262                        ControlAction::StartInput { input: i, .. }
263                        | ControlAction::CancelInput { input: i } => {
264                            matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
265                        },
266                        _ => false,
267                    })
268                    .collect();
269                let inputs = c.inputs.clone();
270
271                (actions, inputs)
272            });
273
274            if is_volume_rider.block.is_controller() {
275                if let Some((actions, inputs)) = inputs {
276                    if let Some(mut character_activity) = character_activities
277                        .get_mut(entity)
278                        .filter(|c| c.steer_dir != inputs.move_dir.y)
279                    {
280                        character_activity.steer_dir = inputs.move_dir.y;
281                    }
282                    match is_volume_rider.pos.kind {
283                        common::mounting::Volume::Entity(uid) => {
284                            if let Some(controller) =
285                                id_maps.uid_entity(uid).and_then(|e| controllers.get_mut(e))
286                            {
287                                controller.inputs = inputs;
288                                controller.actions = actions;
289                            }
290                        },
291                        common::mounting::Volume::Terrain => {},
292                    }
293                }
294            }
295        }
296    }
297}