Skip to main content

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, 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
141                        .ecs()
142                        .read_storage::<rtsim::ActorId>()
143                        .get(rider_entity)
144                        .copied()
145                    && let Some(volume_pos) = volume_pos.try_map_entity(|uid| {
146                        let entity = uid_allocator.uid_entity(uid)?;
147                        state.read_storage::<rtsim::ActorId>().get(entity).copied()
148                    })
149                {
150                    state
151                        .ecs()
152                        .write_resource::<RtSim>()
153                        .hook_character_mount_volume(
154                            &state.ecs().read_resource::<Arc<world::World>>(),
155                            state
156                                .ecs()
157                                .read_resource::<world::IndexOwned>()
158                                .as_index_ref(),
159                            volume_pos,
160                            rider_actor,
161                        );
162                }
163            }
164        }
165    }
166}
167
168fn handle_unmount(server: &mut Server, rider: EcsEntity) {
169    let state = server.state_mut();
170    state.ecs().write_storage::<Is<Rider>>().remove(rider);
171    state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
172}