veloren_server/events/
mounting.rs

1#[cfg(feature = "worldgen")] use std::sync::Arc;
2
3use common::{
4    comp::{self, Agent, CharacterActivity, 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            if (is_pet_of(mount, rider_uid) || is_pet_of(rider, mount_uid)) && can_ride {
76                drop(uids);
77
78                state
79                    .ecs()
80                    .write_storage::<CharacterActivity>()
81                    .get_mut(mount)
82                    .map(|mut activity| activity.is_pet_staying = false);
83
84                state
85                    .ecs()
86                    .write_storage::<Agent>()
87                    .get_mut(mount)
88                    .map(|agent| agent.stay_pos = None);
89
90                let _ = state.link(Mounting {
91                    mount: mount_uid,
92                    rider: rider_uid,
93                });
94            }
95        }
96    }
97}
98
99fn handle_mount_volume(server: &mut Server, rider: EcsEntity, volume_pos: VolumePos) {
100    let state = server.state_mut();
101
102    let mount_mat = volume_pos.get_mount_mat(
103        &state.terrain(),
104        &state.ecs().read_resource(),
105        |e| {
106            state
107                .read_storage()
108                .get(e)
109                .copied()
110                .zip(state.read_storage().get(e).copied())
111        },
112        &state.read_storage(),
113    );
114
115    if let Some((mat, block)) = mount_mat {
116        let mount_pos = mat.mul_point(Vec3::zero());
117        let within_range = {
118            let positions = state.ecs().read_storage::<comp::Pos>();
119            positions.get(rider).is_some_and(|pos| {
120                pos.0.distance_squared(mount_pos) < MAX_SPRITE_MOUNT_RANGE.powi(2)
121            })
122        };
123
124        let maybe_uid = state.ecs().read_storage::<Uid>().get(rider).copied();
125
126        if let Some(rider_uid) = maybe_uid
127            && within_range
128        {
129            let _link_successful = state
130                .link(VolumeMounting {
131                    pos: volume_pos,
132                    block,
133                    rider: rider_uid,
134                })
135                .is_ok();
136            #[cfg(feature = "worldgen")]
137            if _link_successful {
138                let uid_allocator = state.ecs().read_resource::<IdMaps>();
139                if let Some(rider_entity) = uid_allocator.uid_entity(rider_uid)
140                    && let Some(rider_actor) = state.entity_as_actor(rider_entity)
141                    && let Some(volume_pos) = volume_pos.try_map_entity(|uid| {
142                        let entity = uid_allocator.uid_entity(uid)?;
143                        state.read_storage::<RtSimEntity>().get(entity).copied()
144                    })
145                {
146                    state
147                        .ecs()
148                        .write_resource::<RtSim>()
149                        .hook_character_mount_volume(
150                            &state.ecs().read_resource::<Arc<world::World>>(),
151                            state
152                                .ecs()
153                                .read_resource::<world::IndexOwned>()
154                                .as_index_ref(),
155                            volume_pos,
156                            rider_actor,
157                        );
158                }
159            }
160        }
161    }
162}
163
164fn handle_unmount(server: &mut Server, rider: EcsEntity) {
165    let state = server.state_mut();
166    state.ecs().write_storage::<Is<Rider>>().remove(rider);
167    state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
168}