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