veloren_server/sys/
waypoint.rs

1use std::sync::Arc;
2
3use crate::client::Client;
4use common::{
5    comp::{CharacterState, PhysicsState, Player, Pos, Vel, Waypoint, WaypointArea},
6    resources::Time,
7};
8use common_ecs::{Job, Origin, Phase, System};
9use common_net::msg::{Notification, ServerGeneral};
10use specs::{Entities, Join, LendJoin, Read, ReadExpect, ReadStorage, WriteStorage};
11use world::{IndexOwned, World};
12
13/// Cooldown time (in seconds) for "Waypoint Saved" notifications
14const NOTIFY_TIME: f64 = 10.0;
15
16/// This system updates player waypoints
17/// TODO: Make this faster by only considering local waypoints
18/// TODO: Improve reliability of the 'Waypoint Saved' notification
19#[derive(Default)]
20pub struct Sys;
21impl<'a> System<'a> for Sys {
22    type SystemData = (
23        Entities<'a>,
24        ReadStorage<'a, Pos>,
25        ReadStorage<'a, Player>,
26        ReadStorage<'a, WaypointArea>,
27        WriteStorage<'a, Waypoint>,
28        ReadStorage<'a, Client>,
29        Read<'a, Time>,
30        ReadStorage<'a, PhysicsState>,
31        ReadStorage<'a, Vel>,
32        ReadExpect<'a, Arc<World>>,
33        ReadExpect<'a, IndexOwned>,
34        ReadStorage<'a, CharacterState>,
35    );
36
37    const NAME: &'static str = "waypoint";
38    const ORIGIN: Origin = Origin::Server;
39    const PHASE: Phase = Phase::Create;
40
41    fn run(
42        _job: &mut Job<Self>,
43        (
44            entities,
45            positions,
46            players,
47            waypoint_areas,
48            mut waypoints,
49            clients,
50            time,
51            physics_states,
52            velocities,
53            world,
54            index,
55            character_states,
56        ): Self::SystemData,
57    ) {
58        for (entity, player_pos, _, client, physics, velocity, character_state) in (
59            &entities,
60            &positions,
61            &players,
62            &clients,
63            physics_states.maybe(),
64            &velocities,
65            &character_states,
66        )
67            .join()
68        {
69            if character_state.is_sitting()
70                && physics.is_none_or(|ps| ps.on_ground.is_some())
71                && velocity.0.z >= 0.0
72            {
73                for (waypoint_pos, waypoint_area) in (&positions, &waypoint_areas).join() {
74                    if player_pos.0.distance_squared(waypoint_pos.0)
75                        < waypoint_area.radius().powi(2)
76                    {
77                        if let Ok(wp_old) =
78                            waypoints.insert(entity, Waypoint::new(player_pos.0, *time))
79                        {
80                            if wp_old.is_none_or(|w| w.elapsed(*time) > NOTIFY_TIME) {
81                                let location_name = world.get_location_name(
82                                    index.as_index_ref(),
83                                    player_pos.0.xy().as_::<i32>(),
84                                );
85
86                                if let Some(location_name) = location_name {
87                                    client.send_fallible(ServerGeneral::Notification(
88                                        Notification::WaypointSaved { location_name },
89                                    ));
90                                }
91                            }
92                        }
93                    }
94                }
95            }
96        }
97    }
98}