1use crate::{
2 comp::{self, pet::is_mountable, ship::figuredata::VOXEL_COLLIDER_MANIFEST},
3 link::{Is, Link, LinkHandle, Role},
4 terrain::{Block, TerrainGrid},
5 tether,
6 uid::{IdMaps, Uid},
7 vol::ReadVol,
8};
9use hashbrown::HashMap;
10use serde::{Deserialize, Serialize};
11use specs::{
12 Component, Entities, Entity, FlaggedStorage, Read, ReadExpect, ReadStorage, Write,
13 WriteStorage, storage::GenericWriteStorage,
14};
15use vek::*;
16
17#[derive(Serialize, Deserialize, Debug)]
18pub struct Rider;
19
20impl Role for Rider {
21 type Link = Mounting;
22}
23
24#[derive(Serialize, Deserialize, Debug)]
25pub struct Mount;
26
27impl Role for Mount {
28 type Link = Mounting;
29}
30
31#[derive(Serialize, Deserialize, Debug)]
32pub struct Mounting {
33 pub mount: Uid,
34 pub rider: Uid,
35}
36
37#[derive(Debug)]
38pub enum MountingError {
39 NoSuchEntity,
40 NotMountable,
41}
42
43impl Link for Mounting {
44 type CreateData<'a> = (
45 Read<'a, IdMaps>,
46 Entities<'a>,
47 WriteStorage<'a, Is<Mount>>,
48 WriteStorage<'a, Is<Rider>>,
49 ReadStorage<'a, Is<VolumeRider>>,
50 ReadStorage<'a, Is<tether::Follower>>,
51 ReadStorage<'a, comp::Health>,
52 ReadStorage<'a, comp::CharacterState>,
53 );
54 type DeleteData<'a> = (
55 Read<'a, IdMaps>,
56 WriteStorage<'a, Is<Mount>>,
57 WriteStorage<'a, Is<Rider>>,
58 WriteStorage<'a, comp::Pos>,
59 WriteStorage<'a, comp::ForceUpdate>,
60 ReadExpect<'a, TerrainGrid>,
61 );
62 type Error = MountingError;
63 type PersistData<'a> = (
64 Read<'a, IdMaps>,
65 Entities<'a>,
66 ReadStorage<'a, comp::Health>,
67 ReadStorage<'a, comp::Body>,
68 ReadStorage<'a, comp::Mass>,
69 ReadStorage<'a, Is<Mount>>,
70 ReadStorage<'a, Is<Rider>>,
71 ReadStorage<'a, comp::CharacterState>,
72 );
73
74 fn create(
75 this: &LinkHandle<Self>,
76 (
77 id_maps,
78 entities,
79 is_mounts,
80 is_riders,
81 is_volume_rider,
82 is_followers,
83 healths,
84 character_states,
85 ): &mut Self::CreateData<'_>,
86 ) -> Result<(), Self::Error> {
87 let entity = |uid: Uid| id_maps.uid_entity(uid);
88 if this.mount == this.rider {
89 Err(MountingError::NotMountable)
91 } else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
92 let is_alive_and_well = |entity| {
93 entities.is_alive(entity)
94 && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
95 };
96
97 if !is_mounts.contains(mount)
100 && !is_riders.contains(rider)
101 && !is_followers.contains(rider)
102 && (!is_mounts.contains(rider) || !is_riders.contains(mount))
104 && !is_volume_rider.contains(rider)
105 && is_alive_and_well(rider)
107 && is_alive_and_well(rider)
108 {
109 let _ = is_mounts.insert(mount, this.make_role());
110 let _ = is_riders.insert(rider, this.make_role());
111 Ok(())
112 } else {
113 Err(MountingError::NotMountable)
114 }
115 } else {
116 Err(MountingError::NoSuchEntity)
117 }
118 }
119
120 fn persist(
121 this: &LinkHandle<Self>,
122 (id_maps, entities, healths, bodies, masses, is_mounts, is_riders, character_states): &mut Self::PersistData<'_>,
123 ) -> bool {
124 let entity = |uid: Uid| id_maps.uid_entity(uid);
125
126 if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
127 let is_alive_and_well = |entity| {
128 entities.is_alive(entity)
129 && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
130 };
131
132 let is_in_ridable_state = character_states
133 .get(mount)
134 .is_some_and(|cs| !matches!(cs, comp::CharacterState::Roll(_)));
135
136 is_alive_and_well(mount)
138 && is_alive_and_well(rider)
139 && is_mounts.get(mount).is_some()
140 && is_riders.get(rider).is_some()
141 && bodies.get(mount).zip(masses.get(mount)).is_some_and(
142 |(mount_body, mount_mass)| {
143 is_mountable(mount_body, mount_mass, bodies.get(rider), masses.get(rider))
144 },
145 )
146 && is_in_ridable_state
147 } else {
148 false
149 }
150 }
151
152 fn delete(
153 this: &LinkHandle<Self>,
154 (id_maps, is_mounts, is_riders, positions, force_update, terrain): &mut Self::DeleteData<
155 '_,
156 >,
157 ) {
158 let entity = |uid: Uid| id_maps.uid_entity(uid);
159
160 let mount = entity(this.mount);
161 let rider = entity(this.rider);
162
163 mount.map(|mount| is_mounts.remove(mount));
165 rider.map(|rider| is_riders.remove(rider));
166
167 let safe_pos = rider
169 .and_then(|rider| positions.get(rider).copied())
170 .filter(|rider_pos| terrain.is_space(rider_pos.0.map(|e| e.floor() as i32)))
171 .or_else(|| {
172 mount
173 .and_then(|mount| positions.get(mount).copied())
174 .filter(|mount_pos| {
175 terrain.is_space(
176 (mount_pos.0 + Vec3::unit_z() * 0.1).map(|e| e.floor() as i32),
177 )
178 })
179 });
180 rider
181 .and_then(|rider| Some(rider).zip(positions.get_mut(rider)))
182 .map(|(rider, pos)| {
183 let old_pos = pos.0.map(|e| e.floor() as i32);
184 pos.0 = safe_pos
185 .map(|p| p.0.map(|e| e.floor()))
186 .unwrap_or_else(|| terrain.find_ground(old_pos).map(|e| e as f32))
187 + Vec3::new(0.5, 0.5, 0.0);
188 if let Some(force_update) = force_update.get_mut(rider) {
189 force_update.update();
190 }
191 });
192 }
193}
194
195#[derive(Serialize, Deserialize, Debug)]
196pub struct VolumeRider;
197
198impl Role for VolumeRider {
199 type Link = VolumeMounting;
200}
201
202#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
203pub enum Volume<E> {
204 Terrain,
205 Entity(E),
206}
207
208#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
209pub struct VolumePos<E = Uid> {
210 pub kind: Volume<E>,
211 pub pos: Vec3<i32>,
212}
213
214impl<E> VolumePos<E> {
215 pub fn terrain(block_pos: Vec3<i32>) -> Self {
216 Self {
217 kind: Volume::Terrain,
218 pos: block_pos,
219 }
220 }
221
222 pub fn entity(block_pos: Vec3<i32>, uid: E) -> Self {
223 Self {
224 kind: Volume::Entity(uid),
225 pos: block_pos,
226 }
227 }
228
229 pub fn try_map_entity<U>(self, f: impl FnOnce(E) -> Option<U>) -> Option<VolumePos<U>> {
230 Some(VolumePos {
231 pos: self.pos,
232 kind: match self.kind {
233 Volume::Terrain => Volume::Terrain,
234 Volume::Entity(e) => Volume::Entity(f(e)?),
235 },
236 })
237 }
238}
239
240impl VolumePos {
241 pub fn get_block_and_transform(
246 &self,
247 terrain: &TerrainGrid,
248 id_maps: &IdMaps,
249 mut read_pos_and_ori: impl FnMut(Entity) -> Option<(comp::Pos, comp::Ori)>,
250 colliders: &ReadStorage<comp::Collider>,
251 ) -> Option<(Mat4<f32>, Block)> {
252 match self.kind {
253 Volume::Terrain => Some((
254 Mat4::translation_3d(self.pos.as_()),
255 *terrain.get(self.pos).ok()?,
256 )),
257 Volume::Entity(uid) => id_maps.uid_entity(uid).and_then(|entity| {
260 let collider = colliders.get(entity)?;
261 let (pos, ori) = read_pos_and_ori(entity)?;
262
263 let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
264 let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
265
266 let block = *voxel_collider.volume().get(self.pos).ok()?;
267
268 let local_translation = voxel_collider.translation + self.pos.as_();
269
270 let trans = Mat4::from(ori.to_quat()).translated_3d(pos.0)
271 * Mat4::<f32>::translation_3d(local_translation);
272
273 Some((trans, block))
274 }),
275 }
276 }
277
278 pub fn get_block(
280 &self,
281 terrain: &TerrainGrid,
282 id_maps: &IdMaps,
283 colliders: &ReadStorage<comp::Collider>,
284 ) -> Option<Block> {
285 match self.kind {
286 Volume::Terrain => Some(*terrain.get(self.pos).ok()?),
287 Volume::Entity(uid) => id_maps.uid_entity(uid).and_then(|entity| {
288 let collider = colliders.get(entity)?;
289
290 let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
291 let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
292
293 let block = *voxel_collider.volume().get(self.pos).ok()?;
294
295 Some(block)
296 }),
297 }
298 }
299
300 pub fn get_mount_mat(
301 &self,
302 terrain: &TerrainGrid,
303 id_maps: &IdMaps,
304 read_pos_and_ori: impl FnMut(Entity) -> Option<(comp::Pos, comp::Ori)>,
305 colliders: &ReadStorage<comp::Collider>,
306 ) -> Option<(Mat4<f32>, Block)> {
307 let (mut mat, block) =
308 self.get_block_and_transform(terrain, id_maps, read_pos_and_ori, colliders)?;
309
310 let (mount_offset, mount_dir) = block.mount_offset()?;
311 let mount_rot = comp::Ori::from_unnormalized_vec(mount_dir)
312 .unwrap_or_default()
313 .to_quat();
314
315 let rot = block.sprite_z_rot().unwrap_or(0.0);
316 let mirror = block.sprite_mirror_vec();
317
318 mat *= Mat4::from(mount_rot)
319 .translated_3d(mount_offset)
320 .scaled_3d(mirror)
321 .rotated_z(rot)
322 .translated_3d(Vec3::new(0.5, 0.5, 0.0));
323
324 Some((mat, block))
325 }
326}
327
328#[derive(Default, Clone, Serialize, Deserialize, Debug)]
329pub struct VolumeRiders {
330 riders: HashMap<Vec3<i32>, LinkHandle<VolumeMounting>>,
331}
332
333impl VolumeRiders {
334 pub fn clear(&mut self) -> bool {
335 let res = !self.riders.is_empty();
336 self.riders.clear();
337 res
338 }
339
340 pub fn iter_riders(&self) -> impl Iterator<Item = Uid> + '_ {
341 self.riders.values().map(|link| link.rider)
342 }
343
344 pub fn spot_taken(&self, pos: Vec3<i32>) -> bool { self.riders.contains_key(&pos) }
345}
346
347impl Component for VolumeRiders {
348 type Storage = FlaggedStorage<Self>;
349}
350
351#[derive(Serialize, Deserialize, Debug)]
352pub struct VolumeMounting {
353 pub pos: VolumePos,
354 pub block: Block,
355 pub rider: Uid,
356}
357
358impl VolumeMounting {
359 pub fn is_steering_entity(&self) -> bool {
360 matches!(self.pos.kind, Volume::Entity(..)) && self.block.is_controller()
361 }
362}
363
364impl Link for VolumeMounting {
365 type CreateData<'a> = (
366 Entities<'a>,
367 Write<'a, VolumeRiders>,
368 WriteStorage<'a, VolumeRiders>,
369 WriteStorage<'a, Is<VolumeRider>>,
370 ReadStorage<'a, Is<Rider>>,
371 ReadExpect<'a, TerrainGrid>,
372 Read<'a, IdMaps>,
373 ReadStorage<'a, comp::Collider>,
374 ReadStorage<'a, comp::Health>,
375 ReadStorage<'a, comp::CharacterState>,
376 );
377 type DeleteData<'a> = (
378 Write<'a, VolumeRiders>,
379 WriteStorage<'a, VolumeRiders>,
380 WriteStorage<'a, Is<VolumeRider>>,
381 Read<'a, IdMaps>,
382 );
383 type Error = MountingError;
384 type PersistData<'a> = (
385 Entities<'a>,
386 ReadStorage<'a, comp::Health>,
387 Read<'a, VolumeRiders>,
388 ReadStorage<'a, VolumeRiders>,
389 ReadStorage<'a, Is<VolumeRider>>,
390 ReadExpect<'a, TerrainGrid>,
391 Read<'a, IdMaps>,
392 ReadStorage<'a, comp::Collider>,
393 ReadStorage<'a, comp::CharacterState>,
394 );
395
396 fn create(
397 this: &LinkHandle<Self>,
398 (
399 entities,
400 terrain_riders,
401 volume_riders,
402 is_volume_riders,
403 is_riders,
404 terrain_grid,
405 id_maps,
406 colliders,
407 healths,
408 character_states,
409 ): &mut Self::CreateData<'_>,
410 ) -> Result<(), Self::Error> {
411 let entity = |uid: Uid| id_maps.uid_entity(uid);
412 let is_alive_and_well = |entity| {
413 entities.is_alive(entity)
414 && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
415 };
416
417 let riders = match this.pos.kind {
418 Volume::Terrain => &mut *terrain_riders,
419 Volume::Entity(uid) => entity(uid)
420 .filter(|entity| is_alive_and_well(*entity))
421 .and_then(|entity| volume_riders.get_mut_or_default(entity))
422 .ok_or(MountingError::NoSuchEntity)?,
423 };
424 let rider = entity(this.rider).ok_or(MountingError::NoSuchEntity)?;
425
426 if !riders.riders.contains_key(&this.pos.pos)
427 && !is_volume_riders.contains(rider)
428 && !is_volume_riders.contains(rider)
429 && !is_riders.contains(rider)
430 && is_alive_and_well(rider)
431 {
432 let block = this
433 .pos
434 .get_block(terrain_grid, id_maps, colliders)
435 .ok_or(MountingError::NoSuchEntity)?;
436
437 if block == this.block {
438 let _ = is_volume_riders.insert(rider, this.make_role());
439 riders.riders.insert(this.pos.pos, this.clone());
440
441 Ok(())
442 } else {
443 Err(MountingError::NotMountable)
444 }
445 } else {
446 Err(MountingError::NotMountable)
447 }
448 }
449
450 fn persist(
451 this: &LinkHandle<Self>,
452 (
453 entities,
454 healths,
455 terrain_riders,
456 volume_riders,
457 is_volume_riders,
458 terrain_grid,
459 id_maps,
460 colliders,
461 character_states,
462 ): &mut Self::PersistData<'_>,
463 ) -> bool {
464 let entity = |uid: Uid| id_maps.uid_entity(uid);
465 let is_alive_and_well = |entity| {
466 entities.is_alive(entity)
467 && !comp::is_downed_or_dead(healths.get(entity), character_states.get(entity))
468 };
469
470 let riders = match this.pos.kind {
471 Volume::Terrain => &*terrain_riders,
472 Volume::Entity(uid) => {
473 let Some(riders) = entity(uid)
474 .filter(|entity| is_alive_and_well(*entity))
475 .and_then(|entity| volume_riders.get(entity))
476 else {
477 return false;
478 };
479 riders
480 },
481 };
482
483 let rider_exists = entity(this.rider)
484 .is_some_and(|rider| is_volume_riders.contains(rider) && is_alive_and_well(rider));
485 let mount_spot_exists = riders.riders.contains_key(&this.pos.pos);
486
487 let block_exists = this
488 .pos
489 .get_block(terrain_grid, id_maps, colliders)
490 .is_some_and(|block| block == this.block);
491
492 rider_exists && mount_spot_exists && block_exists
493 }
494
495 fn delete(
496 this: &LinkHandle<Self>,
497 (terrain_riders, volume_riders, is_rider, id_maps): &mut Self::DeleteData<'_>,
498 ) {
499 let entity = |uid: Uid| id_maps.uid_entity(uid);
500
501 let riders = match this.pos.kind {
502 Volume::Terrain => Some(&mut **terrain_riders),
503 Volume::Entity(uid) => {
504 entity(uid).and_then(|entity| volume_riders.get_mut_or_default(entity))
505 },
506 };
507
508 if let Some(riders) = riders {
509 riders.riders.remove(&this.pos.pos);
510 }
511
512 if let Some(entity) = entity(this.rider) {
513 is_rider.remove(entity);
514 }
515 }
516}