veloren_common/
mounting.rs

1use crate::{
2    comp::{self, pet::is_mountable, ship::figuredata::VOXEL_COLLIDER_MANIFEST},
3    link::{Is, Link, LinkHandle, Role},
4    terrain::{Block, TerrainGrid},
5    tether,
6    uid::{IdMaps, Uid},
7    vol::ReadVol,
8};
9use hashbrown::HashMap;
10use serde::{Deserialize, Serialize};
11use specs::{
12    Component, Entities, Entity, FlaggedStorage, Read, ReadExpect, ReadStorage, Write,
13    WriteStorage, storage::GenericWriteStorage,
14};
15use vek::*;
16
17#[derive(Serialize, Deserialize, Debug)]
18pub struct Rider;
19
20impl Role for Rider {
21    type Link = Mounting;
22}
23
24#[derive(Serialize, Deserialize, Debug)]
25pub struct Mount;
26
27impl Role for Mount {
28    type Link = Mounting;
29}
30
31#[derive(Serialize, Deserialize, Debug)]
32pub struct Mounting {
33    pub mount: Uid,
34    pub rider: Uid,
35}
36
37#[derive(Debug)]
38pub enum MountingError {
39    NoSuchEntity,
40    NotMountable,
41}
42
43impl Link for Mounting {
44    type CreateData<'a> = (
45        Read<'a, IdMaps>,
46        Entities<'a>,
47        WriteStorage<'a, Is<Mount>>,
48        WriteStorage<'a, Is<Rider>>,
49        ReadStorage<'a, Is<VolumeRider>>,
50        ReadStorage<'a, Is<tether::Follower>>,
51        ReadStorage<'a, comp::Health>,
52        ReadStorage<'a, comp::CharacterState>,
53    );
54    type DeleteData<'a> = (
55        Read<'a, IdMaps>,
56        WriteStorage<'a, Is<Mount>>,
57        WriteStorage<'a, Is<Rider>>,
58        WriteStorage<'a, comp::Pos>,
59        WriteStorage<'a, comp::ForceUpdate>,
60        ReadExpect<'a, TerrainGrid>,
61    );
62    type Error = MountingError;
63    type PersistData<'a> = (
64        Read<'a, IdMaps>,
65        Entities<'a>,
66        ReadStorage<'a, comp::Health>,
67        ReadStorage<'a, comp::Body>,
68        ReadStorage<'a, comp::Mass>,
69        ReadStorage<'a, Is<Mount>>,
70        ReadStorage<'a, Is<Rider>>,
71        ReadStorage<'a, comp::CharacterState>,
72    );
73
74    fn create(
75        this: &LinkHandle<Self>,
76        (
77            id_maps,
78            entities,
79            is_mounts,
80            is_riders,
81            is_volume_rider,
82            is_followers,
83            healths,
84            character_states,
85        ): &mut Self::CreateData<'_>,
86    ) -> Result<(), Self::Error> {
87        let entity = |uid: Uid| id_maps.uid_entity(uid);
88        if this.mount == this.rider {
89            // Forbid self-mounting
90            Err(MountingError::NotMountable)
91        } else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
92            let is_alive_and_well = |entity| {
93                entities.is_alive(entity)
94                    && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
95            };
96
97            // Ensure that neither mount or rider are already part of a mounting
98            // relationship
99            if !is_mounts.contains(mount)
100                && !is_riders.contains(rider)
101                && !is_followers.contains(rider)
102                // TODO: Does this definitely prevent mount cycles?
103                && (!is_mounts.contains(rider) || !is_riders.contains(mount))
104                && !is_volume_rider.contains(rider)
105                // Ensure that both are alive and well.
106                && is_alive_and_well(rider)
107                && is_alive_and_well(rider)
108            {
109                let _ = is_mounts.insert(mount, this.make_role());
110                let _ = is_riders.insert(rider, this.make_role());
111                Ok(())
112            } else {
113                Err(MountingError::NotMountable)
114            }
115        } else {
116            Err(MountingError::NoSuchEntity)
117        }
118    }
119
120    fn persist(
121        this: &LinkHandle<Self>,
122        (id_maps, entities, healths, bodies, masses, is_mounts, is_riders, character_states): &mut Self::PersistData<'_>,
123    ) -> bool {
124        let entity = |uid: Uid| id_maps.uid_entity(uid);
125
126        if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
127            let is_alive_and_well = |entity| {
128                entities.is_alive(entity)
129                    && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
130            };
131
132            let is_in_ridable_state = character_states
133                .get(mount)
134                .is_some_and(|cs| !matches!(cs, comp::CharacterState::Roll(_)));
135
136            // Ensure that both entities are alive and that they continue to be linked
137            is_alive_and_well(mount)
138                && is_alive_and_well(rider)
139                && is_mounts.get(mount).is_some()
140                && is_riders.get(rider).is_some()
141                && bodies.get(mount).zip(masses.get(mount)).is_some_and(
142                    |(mount_body, mount_mass)| {
143                        is_mountable(mount_body, mount_mass, bodies.get(rider), masses.get(rider))
144                    },
145                )
146                && is_in_ridable_state
147        } else {
148            false
149        }
150    }
151
152    fn delete(
153        this: &LinkHandle<Self>,
154        (id_maps, is_mounts, is_riders, positions, force_update, terrain): &mut Self::DeleteData<
155            '_,
156        >,
157    ) {
158        let entity = |uid: Uid| id_maps.uid_entity(uid);
159
160        let mount = entity(this.mount);
161        let rider = entity(this.rider);
162
163        // Delete link components
164        mount.map(|mount| is_mounts.remove(mount));
165        rider.map(|rider| is_riders.remove(rider));
166
167        // Try to move the rider to a safe place when dismounting
168        let safe_pos = rider
169            .and_then(|rider| positions.get(rider).copied())
170            .filter(|rider_pos| terrain.is_space(rider_pos.0.map(|e| e.floor() as i32)))
171            .or_else(|| {
172                mount
173                    .and_then(|mount| positions.get(mount).copied())
174                    .filter(|mount_pos| {
175                        terrain.is_space(
176                            (mount_pos.0 + Vec3::unit_z() * 0.1).map(|e| e.floor() as i32),
177                        )
178                    })
179            });
180        rider
181            .and_then(|rider| Some(rider).zip(positions.get_mut(rider)))
182            .map(|(rider, pos)| {
183                let old_pos = pos.0.map(|e| e.floor() as i32);
184                pos.0 = safe_pos
185                    .map(|p| p.0.map(|e| e.floor()))
186                    .unwrap_or_else(|| terrain.find_ground(old_pos).map(|e| e as f32))
187                    + Vec3::new(0.5, 0.5, 0.0);
188                if let Some(force_update) = force_update.get_mut(rider) {
189                    force_update.update();
190                }
191            });
192    }
193}
194
195#[derive(Serialize, Deserialize, Debug)]
196pub struct VolumeRider;
197
198impl Role for VolumeRider {
199    type Link = VolumeMounting;
200}
201
202#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
203pub enum Volume<E> {
204    Terrain,
205    Entity(E),
206}
207
208#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
209pub struct VolumePos<E = Uid> {
210    pub kind: Volume<E>,
211    pub pos: Vec3<i32>,
212}
213
214impl<E> VolumePos<E> {
215    pub fn terrain(block_pos: Vec3<i32>) -> Self {
216        Self {
217            kind: Volume::Terrain,
218            pos: block_pos,
219        }
220    }
221
222    pub fn entity(block_pos: Vec3<i32>, uid: E) -> Self {
223        Self {
224            kind: Volume::Entity(uid),
225            pos: block_pos,
226        }
227    }
228
229    pub fn try_map_entity<U>(self, f: impl FnOnce(E) -> Option<U>) -> Option<VolumePos<U>> {
230        Some(VolumePos {
231            pos: self.pos,
232            kind: match self.kind {
233                Volume::Terrain => Volume::Terrain,
234                Volume::Entity(e) => Volume::Entity(f(e)?),
235            },
236        })
237    }
238}
239
240impl VolumePos {
241    /// Retrieves the block and matrix transformation for this `VolumeBlock`
242    ///
243    /// The transform is located in the blocks minimum position relative to the
244    /// volume.
245    pub fn get_block_and_transform(
246        &self,
247        terrain: &TerrainGrid,
248        id_maps: &IdMaps,
249        mut read_pos_and_ori: impl FnMut(Entity) -> Option<(comp::Pos, comp::Ori)>,
250        colliders: &ReadStorage<comp::Collider>,
251    ) -> Option<(Mat4<f32>, Block)> {
252        match self.kind {
253            Volume::Terrain => Some((
254                Mat4::translation_3d(self.pos.as_()),
255                *terrain.get(self.pos).ok()?,
256            )),
257            // TODO: theorectically we could store Entity here and translate when syncing over
258            // network?
259            Volume::Entity(uid) => id_maps.uid_entity(uid).and_then(|entity| {
260                let collider = colliders.get(entity)?;
261                let (pos, ori) = read_pos_and_ori(entity)?;
262
263                let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
264                let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
265
266                let block = *voxel_collider.volume().get(self.pos).ok()?;
267
268                let local_translation = voxel_collider.translation + self.pos.as_();
269
270                let trans = Mat4::from(ori.to_quat()).translated_3d(pos.0)
271                    * Mat4::<f32>::translation_3d(local_translation);
272
273                Some((trans, block))
274            }),
275        }
276    }
277
278    /// Get the block at this `VolumePos`.
279    pub fn get_block(
280        &self,
281        terrain: &TerrainGrid,
282        id_maps: &IdMaps,
283        colliders: &ReadStorage<comp::Collider>,
284    ) -> Option<Block> {
285        match self.kind {
286            Volume::Terrain => Some(*terrain.get(self.pos).ok()?),
287            Volume::Entity(uid) => id_maps.uid_entity(uid).and_then(|entity| {
288                let collider = colliders.get(entity)?;
289
290                let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
291                let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
292
293                let block = *voxel_collider.volume().get(self.pos).ok()?;
294
295                Some(block)
296            }),
297        }
298    }
299
300    pub fn get_mount_mat(
301        &self,
302        terrain: &TerrainGrid,
303        id_maps: &IdMaps,
304        read_pos_and_ori: impl FnMut(Entity) -> Option<(comp::Pos, comp::Ori)>,
305        colliders: &ReadStorage<comp::Collider>,
306    ) -> Option<(Mat4<f32>, Block)> {
307        let (mut mat, block) =
308            self.get_block_and_transform(terrain, id_maps, read_pos_and_ori, colliders)?;
309
310        let (mount_offset, mount_dir) = block.mount_offset()?;
311        let mount_rot = comp::Ori::from_unnormalized_vec(mount_dir)
312            .unwrap_or_default()
313            .to_quat();
314
315        let rot = block.sprite_z_rot().unwrap_or(0.0);
316        let mirror = block.sprite_mirror_vec();
317
318        mat *= Mat4::from(mount_rot)
319            .translated_3d(mount_offset)
320            .scaled_3d(mirror)
321            .rotated_z(rot)
322            .translated_3d(Vec3::new(0.5, 0.5, 0.0));
323
324        Some((mat, block))
325    }
326}
327
328#[derive(Default, Clone, Serialize, Deserialize, Debug)]
329pub struct VolumeRiders {
330    riders: HashMap<Vec3<i32>, LinkHandle<VolumeMounting>>,
331}
332
333impl VolumeRiders {
334    pub fn clear(&mut self) -> bool {
335        let res = !self.riders.is_empty();
336        self.riders.clear();
337        res
338    }
339
340    pub fn iter_riders(&self) -> impl Iterator<Item = Uid> + '_ {
341        self.riders.values().map(|link| link.rider)
342    }
343
344    pub fn spot_taken(&self, pos: Vec3<i32>) -> bool { self.riders.contains_key(&pos) }
345}
346
347impl Component for VolumeRiders {
348    type Storage = FlaggedStorage<Self>;
349}
350
351#[derive(Serialize, Deserialize, Debug)]
352pub struct VolumeMounting {
353    pub pos: VolumePos,
354    pub block: Block,
355    pub rider: Uid,
356}
357
358impl VolumeMounting {
359    pub fn is_steering_entity(&self) -> bool {
360        matches!(self.pos.kind, Volume::Entity(..)) && self.block.is_controller()
361    }
362}
363
364impl Link for VolumeMounting {
365    type CreateData<'a> = (
366        Entities<'a>,
367        Write<'a, VolumeRiders>,
368        WriteStorage<'a, VolumeRiders>,
369        WriteStorage<'a, Is<VolumeRider>>,
370        ReadStorage<'a, Is<Rider>>,
371        ReadExpect<'a, TerrainGrid>,
372        Read<'a, IdMaps>,
373        ReadStorage<'a, comp::Collider>,
374        ReadStorage<'a, comp::Health>,
375        ReadStorage<'a, comp::CharacterState>,
376    );
377    type DeleteData<'a> = (
378        Write<'a, VolumeRiders>,
379        WriteStorage<'a, VolumeRiders>,
380        WriteStorage<'a, Is<VolumeRider>>,
381        Read<'a, IdMaps>,
382    );
383    type Error = MountingError;
384    type PersistData<'a> = (
385        Entities<'a>,
386        ReadStorage<'a, comp::Health>,
387        Read<'a, VolumeRiders>,
388        ReadStorage<'a, VolumeRiders>,
389        ReadStorage<'a, Is<VolumeRider>>,
390        ReadExpect<'a, TerrainGrid>,
391        Read<'a, IdMaps>,
392        ReadStorage<'a, comp::Collider>,
393        ReadStorage<'a, comp::CharacterState>,
394    );
395
396    fn create(
397        this: &LinkHandle<Self>,
398        (
399            entities,
400            terrain_riders,
401            volume_riders,
402            is_volume_riders,
403            is_riders,
404            terrain_grid,
405            id_maps,
406            colliders,
407            healths,
408            character_states,
409        ): &mut Self::CreateData<'_>,
410    ) -> Result<(), Self::Error> {
411        let entity = |uid: Uid| id_maps.uid_entity(uid);
412        let is_alive_and_well = |entity| {
413            entities.is_alive(entity)
414                && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
415        };
416
417        let riders = match this.pos.kind {
418            Volume::Terrain => &mut *terrain_riders,
419            Volume::Entity(uid) => entity(uid)
420                .filter(|entity| is_alive_and_well(*entity))
421                .and_then(|entity| volume_riders.get_mut_or_default(entity))
422                .ok_or(MountingError::NoSuchEntity)?,
423        };
424        let rider = entity(this.rider).ok_or(MountingError::NoSuchEntity)?;
425
426        if !riders.riders.contains_key(&this.pos.pos)
427            && !is_volume_riders.contains(rider)
428            && !is_volume_riders.contains(rider)
429            && !is_riders.contains(rider)
430            && is_alive_and_well(rider)
431        {
432            let block = this
433                .pos
434                .get_block(terrain_grid, id_maps, colliders)
435                .ok_or(MountingError::NoSuchEntity)?;
436
437            if block == this.block {
438                let _ = is_volume_riders.insert(rider, this.make_role());
439                riders.riders.insert(this.pos.pos, this.clone());
440
441                Ok(())
442            } else {
443                Err(MountingError::NotMountable)
444            }
445        } else {
446            Err(MountingError::NotMountable)
447        }
448    }
449
450    fn persist(
451        this: &LinkHandle<Self>,
452        (
453            entities,
454            healths,
455            terrain_riders,
456            volume_riders,
457            is_volume_riders,
458            terrain_grid,
459            id_maps,
460            colliders,
461            character_states,
462        ): &mut Self::PersistData<'_>,
463    ) -> bool {
464        let entity = |uid: Uid| id_maps.uid_entity(uid);
465        let is_alive_and_well = |entity| {
466            entities.is_alive(entity)
467                && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
468        };
469
470        let riders = match this.pos.kind {
471            Volume::Terrain => &*terrain_riders,
472            Volume::Entity(uid) => {
473                let Some(riders) = entity(uid)
474                    .filter(|entity| is_alive_and_well(*entity))
475                    .and_then(|entity| volume_riders.get(entity))
476                else {
477                    return false;
478                };
479                riders
480            },
481        };
482
483        let rider_exists = entity(this.rider)
484            .is_some_and(|rider| is_volume_riders.contains(rider) && is_alive_and_well(rider));
485        let mount_spot_exists = riders.riders.contains_key(&this.pos.pos);
486
487        let block_exists = this
488            .pos
489            .get_block(terrain_grid, id_maps, colliders)
490            .is_some_and(|block| block == this.block);
491
492        rider_exists && mount_spot_exists && block_exists
493    }
494
495    fn delete(
496        this: &LinkHandle<Self>,
497        (terrain_riders, volume_riders, is_rider, id_maps): &mut Self::DeleteData<'_>,
498    ) {
499        let entity = |uid: Uid| id_maps.uid_entity(uid);
500
501        let riders = match this.pos.kind {
502            Volume::Terrain => Some(&mut **terrain_riders),
503            Volume::Entity(uid) => {
504                entity(uid).and_then(|entity| volume_riders.get_mut_or_default(entity))
505            },
506        };
507
508        if let Some(riders) = riders {
509            riders.riders.remove(&this.pos.pos);
510        }
511
512        if let Some(entity) = entity(this.rider) {
513            is_rider.remove(entity);
514        }
515    }
516}