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