1use common::{
2 combat::RiderEffects,
3 comp::{
4 Body, Buff, BuffCategory, BuffChange, Buffs, CharacterActivity, CharacterState, Collider,
5 ControlAction, Controller, InputKind, Mass, Ori, PhysicsState, Pos, Scale, Stats, Vel,
6 buff::DestInfo,
7 },
8 event::{BuffEvent, EmitExt},
9 event_emitters,
10 link::Is,
11 mounting::{Mount, Rider, VolumeRider},
12 resources::Time,
13 terrain::TerrainGrid,
14 uid::IdMaps,
15};
16use common_ecs::{Job, Origin, Phase, System};
17use specs::{Entities, Join, LendJoin, Read, ReadExpect, ReadStorage, WriteStorage};
18use vek::*;
19
20event_emitters! {
21 struct Events[EventEmitters] {
22 buff: BuffEvent,
23 }
24}
25
26#[derive(Default)]
28pub struct Sys;
29impl<'a> System<'a> for Sys {
30 type SystemData = (
31 Read<'a, IdMaps>,
32 Read<'a, Time>,
33 ReadExpect<'a, TerrainGrid>,
34 Events<'a>,
35 Entities<'a>,
36 WriteStorage<'a, Controller>,
37 ReadStorage<'a, Is<Rider>>,
38 ReadStorage<'a, Is<Mount>>,
39 ReadStorage<'a, Is<VolumeRider>>,
40 WriteStorage<'a, Pos>,
41 WriteStorage<'a, Vel>,
42 WriteStorage<'a, Ori>,
43 WriteStorage<'a, CharacterActivity>,
44 WriteStorage<'a, PhysicsState>,
45 ReadStorage<'a, Body>,
46 ReadStorage<'a, Scale>,
47 ReadStorage<'a, Collider>,
48 ReadStorage<'a, Buffs>,
49 ReadStorage<'a, Stats>,
50 ReadStorage<'a, Mass>,
51 ReadStorage<'a, RiderEffects>,
52 ReadStorage<'a, CharacterState>,
53 );
54
55 const NAME: &'static str = "mount";
56 const ORIGIN: Origin = Origin::Common;
57 const PHASE: Phase = Phase::Create;
58
59 fn run(
60 _job: &mut Job<Self>,
61 (
62 id_maps,
63 time,
64 terrain,
65 events,
66 entities,
67 mut controllers,
68 is_riders,
69 is_mounts,
70 is_volume_riders,
71 mut positions,
72 mut velocities,
73 mut orientations,
74 mut character_activities,
75 mut physics_states,
76 bodies,
77 scales,
78 colliders,
79 buffs,
80 stats,
81 masses,
82 rider_effects,
83 char_states,
84 ): Self::SystemData,
85 ) {
86 let mut emitters = events.get_emitters();
87 for (entity, is_mount, body, rider_effects) in
89 (&entities, &is_mounts, bodies.maybe(), rider_effects.maybe()).join()
90 {
91 let Some(rider_entity) = id_maps.uid_entity(is_mount.rider) else {
92 continue;
93 };
94
95 if let Some(rider_effects) = rider_effects
97 && let Some(target_buffs) = buffs.get(rider_entity)
98 {
99 for effect in rider_effects.0.iter() {
100 let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
101 buff.cat_ids.iter()
102 .any(|cat_id| matches!(cat_id, BuffCategory::FromLink(link) if link.is_link(is_mount.get_link())))
103 && buff.kind == effect.kind && buff.data.strength >= effect.data.strength
104 });
105
106 if emit_buff {
107 let dest_info = DestInfo {
108 stats: stats.get(rider_entity),
109 mass: masses.get(rider_entity),
110 };
111 let mut cat_ids = effect.cat_ids.clone();
112 cat_ids.push(BuffCategory::FromLink(
113 is_mount.get_link().downgrade().into_dyn(),
114 ));
115
116 emitters.emit(BuffEvent {
117 entity: rider_entity,
118 buff_change: BuffChange::Add(Buff::new(
119 effect.kind,
120 effect.data,
121 cat_ids,
122 common::comp::BuffSource::Character {
123 by: is_mount.mount,
124 tool_kind: None,
125 },
126 *time,
127 dest_info,
128 masses.get(entity),
129 None,
133 )),
134 });
135 }
136 }
137 }
138 let Some(inputs_and_actions) = controllers.get_mut(rider_entity).map(|c| {
140 if body.is_some_and(|b| !b.has_free_will()) {
143 let actions = c
144 .actions
145 .extract_if(.., |action| match action {
146 ControlAction::StartInput { input: i, .. }
147 | ControlAction::CancelInput { input: i } => {
148 matches!(
149 i,
150 InputKind::Jump
151 | InputKind::WallJump
152 | InputKind::Fly
153 | InputKind::Roll
154 )
155 },
156 _ => false,
157 })
158 .collect();
159 Some((c.inputs.clone(), actions))
160 } else {
161 None
162 }
163 }) else {
164 continue;
165 };
166
167 let pos = positions.get(entity).copied();
169 let ori = orientations.get(entity).copied();
170 let vel = velocities.get(entity).copied();
171 if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
172 let mounter_body = bodies.get(rider_entity);
173 let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset)
174 * scales.get(entity).map_or(1.0, |s| s.0)
175 + mounter_body.map_or(Vec3::zero(), Body::rider_offset)
176 * scales.get(rider_entity).map_or(1.0, |s| s.0);
177 let _ =
178 positions.insert(rider_entity, Pos(pos.0 + ori.to_quat() * mounting_offset));
179
180 let should_set_ori = char_states
183 .get(rider_entity)
184 .is_none_or(|cs| !cs.can_look_while_mounted());
185
186 if should_set_ori {
187 let _ = orientations.insert(rider_entity, ori);
188 }
189
190 let _ = velocities.insert(rider_entity, vel);
191 }
192 if let Some((inputs, actions)) = inputs_and_actions
194 && let Some(controller) = controllers.get_mut(entity)
195 {
196 controller.inputs = inputs;
197 controller.actions = actions;
198 }
199 }
200
201 for (physics_state, _) in (
205 &mut physics_states,
206 is_riders.mask() | is_volume_riders.mask(),
207 )
208 .join()
209 {
210 *physics_state = PhysicsState::default();
211 }
212
213 for (entity, is_volume_rider) in (&entities, &is_volume_riders).join() {
215 if let Some((mat, _)) = is_volume_rider.pos.get_mount_mat(
216 &terrain,
217 &id_maps,
218 |e| positions.get(e).copied().zip(orientations.get(e).copied()),
219 &colliders,
220 ) {
221 if let Some(pos) = positions.get_mut(entity) {
222 pos.0 = mat.mul_point(Vec3::zero());
223 }
224 if let Some(ori) = orientations.get_mut(entity) {
225 *ori = Ori::from_unnormalized_vec(mat.mul_direction(Vec3::unit_y()))
226 .unwrap_or_default();
227 }
228 }
229 let v = match is_volume_rider.pos.kind {
230 common::mounting::Volume::Terrain => Vec3::zero(),
231 common::mounting::Volume::Entity(uid) => {
232 if let Some(v) = id_maps.uid_entity(uid).and_then(|e| velocities.get(e)) {
233 v.0
234 } else {
235 Vec3::zero()
236 }
237 },
238 };
239 if let Some(vel) = velocities.get_mut(entity) {
240 vel.0 = v;
241 }
242
243 if let Some(target_buffs) = buffs.get(entity)
249 && let Some(block_buffs) = is_volume_rider.block.mount_buffs()
250 {
251 for effect in block_buffs.iter() {
252 let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
253 buff.cat_ids.iter()
254 .any(|cat_id| matches!(cat_id, BuffCategory::FromLink(link) if link.is_link(is_volume_rider.get_link())))
255 && buff.kind == effect.kind && buff.data.strength >= effect.data.strength
256 });
257
258 if emit_buff {
259 let dest_info = DestInfo {
260 stats: stats.get(entity),
261 mass: masses.get(entity),
262 };
263 let mut cat_ids = effect.cat_ids.clone();
264 cat_ids.push(BuffCategory::FromLink(
265 is_volume_rider.get_link().downgrade().into_dyn(),
266 ));
267
268 emitters.emit(BuffEvent {
269 entity,
270 buff_change: BuffChange::Add(Buff::new(
271 effect.kind,
272 effect.data,
273 cat_ids,
274 common::comp::BuffSource::Block,
275 *time,
276 dest_info,
277 masses.get(entity),
278 None,
282 )),
283 });
284 }
285 }
286 }
287
288 let inputs = controllers.get_mut(entity).map(|c| {
289 let actions: Vec<_> = c
290 .actions
291 .extract_if(.., |action| match action {
292 ControlAction::StartInput { input: i, .. }
293 | ControlAction::CancelInput { input: i } => {
294 matches!(
295 i,
296 InputKind::Jump
297 | InputKind::WallJump
298 | InputKind::Fly
299 | InputKind::Roll
300 )
301 },
302 _ => false,
303 })
304 .collect();
305 let inputs = c.inputs.clone();
306
307 (actions, inputs)
308 });
309
310 if is_volume_rider.block.is_controller()
311 && let Some((actions, inputs)) = inputs
312 {
313 if let Some(mut character_activity) = character_activities
314 .get_mut(entity)
315 .filter(|c| c.steer_dir != inputs.move_dir.y)
316 {
317 character_activity.steer_dir = inputs.move_dir.y;
318 }
319 match is_volume_rider.pos.kind {
320 common::mounting::Volume::Entity(uid) => {
321 if let Some(controller) =
322 id_maps.uid_entity(uid).and_then(|e| controllers.get_mut(e))
323 {
324 controller.inputs = inputs;
325 controller.actions = actions;
326 }
327 },
328 common::mounting::Volume::Terrain => {},
329 }
330 }
331 }
332 }
333}