Skip to main content

veloren_server/sys/msg/
gizmos.rs

1use common::{
2    CachedSpatialGrid,
3    comp::{
4        self, GizmoSubscriber,
5        gizmos::{GizmoSubscription, Gizmos, RtsimGizmos},
6    },
7    resources::Time,
8    rtsim,
9    uid::IdMaps,
10};
11use common_ecs::{Job, Origin, Phase, System};
12use common_net::msg::ServerGeneral;
13use hashbrown::HashSet;
14use specs::{Entity, Join, Read, ReadExpect, ReadStorage, WriteExpect, shred};
15use vek::{Rgb, Rgba, Vec3};
16
17use crate::client::Client;
18
19#[derive(specs::SystemData)]
20pub struct ReadData<'a> {
21    id_maps: Read<'a, IdMaps>,
22    time: Read<'a, Time>,
23    spatial_grid: ReadExpect<'a, CachedSpatialGrid>,
24    subscribers: ReadStorage<'a, GizmoSubscriber>,
25    agents: ReadStorage<'a, comp::Agent>,
26    position: ReadStorage<'a, comp::Pos>,
27    rtsim_actors: ReadStorage<'a, rtsim::ActorId>,
28    client: ReadStorage<'a, Client>,
29}
30
31struct RtsimGizmoTracker<'a> {
32    gizmos: &'a mut RtsimGizmos,
33    request: HashSet<rtsim::ActorId>,
34}
35
36impl RtsimGizmoTracker<'_> {
37    fn get(&mut self, actor: rtsim::ActorId) -> impl Iterator<Item = Gizmos> + use<'_> {
38        self.request.insert(actor);
39
40        self.gizmos
41            .tracked
42            .get(actor)
43            .into_iter()
44            .flatten()
45            .cloned()
46    }
47}
48
49#[derive(Default)]
50pub struct Sys;
51
52impl<'a> System<'a> for Sys {
53    type SystemData = (ReadData<'a>, WriteExpect<'a, RtsimGizmos>);
54
55    const NAME: &'static str = "msg::gizmos";
56    const ORIGIN: Origin = Origin::Server;
57    const PHASE: Phase = Phase::Create;
58
59    fn run(_job: &mut Job<Self>, (data, mut rtsim_gizmos): Self::SystemData) {
60        let mut tracker = RtsimGizmoTracker {
61            gizmos: &mut rtsim_gizmos,
62            request: HashSet::new(),
63        };
64
65        for (subscriber, client, pos) in (&data.subscribers, &data.client, &data.position).join() {
66            let mut gizmos = Vec::new();
67            for (kind, target) in subscriber.gizmos.iter() {
68                match target {
69                    comp::gizmos::GizmoContext::Disabled => {},
70                    comp::gizmos::GizmoContext::Enabled => {
71                        gizmos_for(
72                            &mut gizmos,
73                            kind,
74                            *pos,
75                            subscriber.range,
76                            &data,
77                            &mut tracker,
78                        );
79                    },
80                    comp::gizmos::GizmoContext::EnabledWithTarget(uid) => {
81                        if let Some(target) = data.id_maps.uid_entity(*uid) {
82                            gizmos_for_target(
83                                &mut gizmos,
84                                kind,
85                                target,
86                                subscriber.range,
87                                &data,
88                                &mut tracker,
89                            )
90                        }
91                    },
92                }
93            }
94
95            if !gizmos.is_empty() {
96                client.send_fallible(ServerGeneral::Gizmos(gizmos));
97            }
98        }
99
100        tracker.gizmos.tracked.retain(|id, buffer| {
101            buffer.clear();
102            tracker.request.remove(&id)
103        });
104
105        for actor in tracker.request {
106            tracker.gizmos.tracked.insert(actor, Vec::new());
107        }
108    }
109}
110
111fn pathfind_gizmos(gizmos: &mut Vec<Gizmos>, pos: &comp::Pos, agent: &comp::Agent, time: &Time) {
112    if time.0 - agent.chaser.last_update_time().0 > 1.0 {
113        return;
114    }
115    if let Some(route) = agent.chaser.get_route() {
116        if let Some(traversed) = route
117            .get_path()
118            .nodes
119            .get(..route.next_idx())
120            .filter(|n| n.len() >= 2)
121        {
122            gizmos.push(Gizmos::line_strip(
123                traversed.iter().map(|p| p.as_() + 0.5).collect(),
124                Rgba::new(255, 255, 255, 100),
125            ));
126        }
127        if let Some(to_traverse) = route
128            .get_path()
129            .nodes
130            .get(route.next_idx().saturating_sub(1)..)
131            .filter(|n| n.len() >= 2)
132        {
133            gizmos.push(Gizmos::line_strip(
134                to_traverse.iter().map(|p| p.as_() + 0.5).collect(),
135                Rgb::red(),
136            ));
137        }
138    }
139    let above = pos.0 + Vec3::unit_z() * 2.0;
140    if let Some(target) = agent.chaser.last_target() {
141        gizmos.push(Gizmos::line(above, target, Rgba::new(0, 0, 255, 200)));
142        gizmos.push(Gizmos::sphere(target, 0.3, Rgba::new(255, 0, 0, 200)));
143    }
144    let (length, state) = agent.chaser.state();
145
146    let size = match length {
147        common::path::PathLength::Small => 0.1,
148        common::path::PathLength::Medium => 0.3,
149        common::path::PathLength::Long => 0.6,
150        common::path::PathLength::Longest => 0.9,
151    };
152
153    let color = match state {
154        common::path::PathState::None => Rgba::new(255, 0, 0, 200),
155        common::path::PathState::Exhausted => Rgba::new(255, 255, 0, 200),
156        common::path::PathState::Pending => Rgba::new(255, 0, 255, 200),
157        common::path::PathState::Path => Rgba::new(0, 255, 0, 200),
158    };
159    gizmos.push(Gizmos::sphere(above, size, color));
160}
161
162fn rtsim_gizmos(
163    gizmos: &mut Vec<Gizmos>,
164    actor: rtsim::ActorId,
165    rtsim_tracker: &mut RtsimGizmoTracker,
166) {
167    gizmos.extend(rtsim_tracker.get(actor));
168}
169
170fn gizmos_for_target(
171    gizmos: &mut Vec<Gizmos>,
172    subscription: GizmoSubscription,
173    target: Entity,
174    _range: f32,
175    data: &ReadData,
176    rtsim_tracker: &mut RtsimGizmoTracker,
177) {
178    match subscription {
179        GizmoSubscription::PathFinding => {
180            if let Some(agent) = data.agents.get(target)
181                && let Some(pos) = data.position.get(target)
182            {
183                pathfind_gizmos(gizmos, pos, agent, &data.time);
184            }
185        },
186        GizmoSubscription::Rtsim => {
187            if let Some(actor) = data.rtsim_actors.get(target) {
188                rtsim_gizmos(gizmos, *actor, rtsim_tracker);
189            }
190        },
191    }
192}
193
194fn gizmos_for(
195    gizmos: &mut Vec<Gizmos>,
196    subscription: GizmoSubscription,
197    pos: comp::Pos,
198    range: f32,
199    data: &ReadData,
200    rtsim_tracker: &mut RtsimGizmoTracker,
201) {
202    for target in data.spatial_grid.0.in_circle_aabr(pos.0.xy(), range) {
203        gizmos_for_target(gizmos, subscription, target, range, data, rtsim_tracker);
204    }
205}