veloren_server/events/
mounting.rs

1#[cfg(feature = "worldgen")] use std::sync::Arc;
2
3use common::{
4    comp::{self, pet::is_mountable},
5    consts::{MAX_MOUNT_RANGE, MAX_SPRITE_MOUNT_RANGE},
6    event::{MountEvent, MountVolumeEvent, UnmountEvent},
7    link::Is,
8    mounting::{Mounting, Rider, VolumeMounting, VolumeRider},
9    uid::Uid,
10};
11#[cfg(feature = "worldgen")]
12use common::{rtsim::RtSimEntity, uid::IdMaps};
13use specs::WorldExt;
14
15#[cfg(feature = "worldgen")]
16use crate::rtsim::RtSim;
17use crate::{Server, state_ext::StateExt};
18
19pub fn within_mounting_range(
20    player_position: Option<&comp::Pos>,
21    mount_position: Option<&comp::Pos>,
22) -> bool {
23    match (player_position, mount_position) {
24        (Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_MOUNT_RANGE.powi(2),
25        _ => false,
26    }
27}
28
29pub fn handle_mount(server: &mut Server, MountEvent(rider, mount): MountEvent) {
30    let state = server.state_mut();
31
32    let within_range = {
33        let positions = state.ecs().read_storage::<comp::Pos>();
34        within_mounting_range(positions.get(rider), positions.get(mount))
35    };
36
37    if within_range {
38        let uids = state.ecs().read_storage::<Uid>();
39        if let (Some(rider_uid), Some(mount_uid)) =
40            (uids.get(rider).copied(), uids.get(mount).copied())
41        {
42            let is_pet_of = |mount, rider_uid| {
43                matches!(
44                    state
45                        .ecs()
46                        .read_storage::<comp::Alignment>()
47                        .get(mount),
48                    Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
49                )
50            };
51
52            let can_ride = state
53                .ecs()
54                .read_storage()
55                .get(mount)
56                .zip(state.ecs().read_storage().get(mount))
57                .is_some_and(|(mount_body, mount_mass)| {
58                    is_mountable(
59                        mount_body,
60                        mount_mass,
61                        state.ecs().read_storage().get(rider),
62                        state.ecs().read_storage().get(rider),
63                    )
64                });
65
66            let is_stay = state
67                .ecs()
68                .read_storage::<comp::Agent>()
69                .get(mount)
70                .and_then(|x| x.stay_pos)
71                .is_some();
72
73            if (is_pet_of(mount, rider_uid) || is_pet_of(rider, mount_uid)) && can_ride && !is_stay
74            {
75                drop(uids);
76                let _ = state.link(Mounting {
77                    mount: mount_uid,
78                    rider: rider_uid,
79                });
80            }
81        }
82    }
83}
84
85pub fn handle_mount_volume(
86    server: &mut Server,
87    MountVolumeEvent(rider, volume_pos): MountVolumeEvent,
88) {
89    let state = server.state_mut();
90
91    let block_transform = volume_pos.get_block_and_transform(
92        &state.terrain(),
93        &state.ecs().read_resource(),
94        |e| {
95            state
96                .read_storage()
97                .get(e)
98                .copied()
99                .zip(state.read_storage().get(e).copied())
100        },
101        &state.read_storage(),
102    );
103
104    if let Some((mat, _, block)) = block_transform
105        && let Some(mount_offset) = block.mount_offset()
106    {
107        let mount_pos = (mat * mount_offset.0.with_w(1.0)).xyz();
108        let within_range = {
109            let positions = state.ecs().read_storage::<comp::Pos>();
110            positions.get(rider).is_some_and(|pos| {
111                pos.0.distance_squared(mount_pos) < MAX_SPRITE_MOUNT_RANGE.powi(2)
112            })
113        };
114
115        let maybe_uid = state.ecs().read_storage::<Uid>().get(rider).copied();
116
117        if let Some(rider) = maybe_uid
118            && within_range
119        {
120            let _link_successful = state
121                .link(VolumeMounting {
122                    pos: volume_pos,
123                    block,
124                    rider,
125                })
126                .is_ok();
127            #[cfg(feature = "worldgen")]
128            if _link_successful {
129                let uid_allocator = state.ecs().read_resource::<IdMaps>();
130                if let Some(rider_entity) = uid_allocator.uid_entity(rider)
131                    && let Some(rider_actor) = state.entity_as_actor(rider_entity)
132                    && let Some(volume_pos) = volume_pos.try_map_entity(|uid| {
133                        let entity = uid_allocator.uid_entity(uid)?;
134                        state.read_storage::<RtSimEntity>().get(entity).map(|v| v.0)
135                    })
136                {
137                    state
138                        .ecs()
139                        .write_resource::<RtSim>()
140                        .hook_character_mount_volume(
141                            &state.ecs().read_resource::<Arc<world::World>>(),
142                            state
143                                .ecs()
144                                .read_resource::<world::IndexOwned>()
145                                .as_index_ref(),
146                            volume_pos,
147                            rider_actor,
148                        );
149                }
150            }
151        }
152    }
153}
154
155pub fn handle_unmount(server: &mut Server, UnmountEvent(rider): UnmountEvent) {
156    let state = server.state_mut();
157    state.ecs().write_storage::<Is<Rider>>().remove(rider);
158    state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
159}