veloren_server/sys/
pets.rs

1use common::{
2    comp::{Agent, Alignment, Pet, PhysicsState, Pos},
3    terrain::TerrainGrid,
4    uid::IdMaps,
5};
6use common_ecs::{Job, Origin, Phase, System};
7use specs::{Entities, Entity, Join, Read, ReadExpect, ReadStorage, WriteStorage};
8
9/// This system is responsible for handling pets
10#[derive(Default)]
11pub struct Sys;
12impl<'a> System<'a> for Sys {
13    type SystemData = (
14        Entities<'a>,
15        ReadExpect<'a, TerrainGrid>,
16        WriteStorage<'a, Pos>,
17        ReadStorage<'a, Alignment>,
18        ReadStorage<'a, Pet>,
19        ReadStorage<'a, Agent>,
20        ReadStorage<'a, PhysicsState>,
21        Read<'a, IdMaps>,
22    );
23
24    const NAME: &'static str = "pets";
25    const ORIGIN: Origin = Origin::Server;
26    const PHASE: Phase = Phase::Create;
27
28    fn run(
29        _job: &mut Job<Self>,
30        (entities, terrain, mut positions, alignments, pets, agn, physics, id_maps): Self::SystemData,
31    ) {
32        const LOST_PET_DISTANCE_THRESHOLD: f32 = 200.0;
33
34        // Find pets that are too far away from their owner
35        let lost_pets: Vec<(Entity, Pos)> = (&entities, &positions, &alignments, &pets)
36            .join()
37            .filter_map(|(entity, pos, alignment, _)| match alignment {
38                Alignment::Owned(owner_uid) => Some((entity, pos, *owner_uid)),
39                _ => None,
40            })
41            .filter_map(|(pet_entity, pet_pos, owner_uid)| {
42                id_maps.uid_entity(owner_uid).and_then(|owner_entity| {
43                    match (positions.get(owner_entity), physics.get(owner_entity)) {
44                        (Some(position), Some(physics)) => {
45                            Some((pet_entity, position, physics, pet_pos))
46                        },
47                        _ => None,
48                    }
49                })
50            })
51            .filter(|(_, owner_pos, owner_physics, pet_pos)| {
52                // Don't teleport pets to the player if they're in the air, nobody wants
53                // pets to go splat :(
54                owner_physics.on_ground.is_some()
55                    && owner_pos.0.distance_squared(pet_pos.0) > LOST_PET_DISTANCE_THRESHOLD.powi(2)
56            })
57            .map(|(entity, owner_pos, _, _)| (entity, *owner_pos))
58            .collect();
59
60        for (pet_entity, owner_pos) in lost_pets.iter() {
61            let stay = agn.get(*pet_entity).and_then(|x| x.stay_pos).is_some();
62            if let Some(pet_pos) = positions.get_mut(*pet_entity)
63                && !stay
64            {
65                // Move the pets to their owner's position
66                // TODO: Create a teleportation event to handle this instead of
67                // processing the entity position move here
68                pet_pos.0 = terrain
69                    .find_ground(owner_pos.0.map(|e| e.floor() as i32))
70                    .map(|e| e as f32);
71            }
72        }
73    }
74}