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