veloren_server/events/
mounting.rs1#[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}