veloren_server/sys/msg/
gizmos.rs

1use common::{
2    CachedSpatialGrid,
3    comp::{
4        self, GizmoSubscriber,
5        gizmos::{GizmoSubscription, Gizmos},
6    },
7    uid::IdMaps,
8};
9use common_ecs::{Job, Origin, Phase, System};
10use common_net::msg::ServerGeneral;
11use specs::{Entity, Join, Read, ReadExpect, ReadStorage, shred};
12use vek::{Rgb, Rgba};
13
14use crate::client::Client;
15
16#[derive(specs::SystemData)]
17pub struct ReadData<'a> {
18    id_maps: Read<'a, IdMaps>,
19    spatial_grid: ReadExpect<'a, CachedSpatialGrid>,
20    subscribers: ReadStorage<'a, GizmoSubscriber>,
21    agents: ReadStorage<'a, comp::Agent>,
22    position: ReadStorage<'a, comp::Pos>,
23    client: ReadStorage<'a, Client>,
24}
25
26#[derive(Default)]
27pub struct Sys;
28
29impl<'a> System<'a> for Sys {
30    type SystemData = ReadData<'a>;
31
32    const NAME: &'static str = "msg::gizmos";
33    const ORIGIN: Origin = Origin::Server;
34    const PHASE: Phase = Phase::Create;
35
36    fn run(_job: &mut Job<Self>, data: Self::SystemData) {
37        for (subscriber, client, pos) in (&data.subscribers, &data.client, &data.position).join() {
38            let mut gizmos = Vec::new();
39            for (kind, target) in subscriber.gizmos.iter() {
40                match target {
41                    comp::gizmos::GizmoContext::Disabled => {},
42                    comp::gizmos::GizmoContext::Enabled => {
43                        gizmos_for(&mut gizmos, kind, *pos, subscriber.range, &data);
44                    },
45                    comp::gizmos::GizmoContext::EnabledWithTarget(uid) => {
46                        if let Some(target) = data.id_maps.uid_entity(*uid) {
47                            gizmos_for_target(&mut gizmos, kind, target, subscriber.range, &data)
48                        }
49                    },
50                }
51            }
52
53            if !gizmos.is_empty() {
54                client.send_fallible(ServerGeneral::Gizmos(gizmos));
55            }
56        }
57    }
58}
59
60fn pathfind_gizmos(gizmos: &mut Vec<Gizmos>, agent: &comp::Agent) {
61    if let Some(route) = agent.chaser.get_route() {
62        if let Some(traversed) = route
63            .get_path()
64            .nodes
65            .get(..route.next_idx())
66            .filter(|n| n.len() >= 2)
67        {
68            gizmos.push(Gizmos::line_strip(
69                traversed.iter().map(|p| p.as_() + 0.5).collect(),
70                Rgba::new(255, 255, 255, 100),
71            ));
72        }
73        if let Some(to_traverse) = route
74            .get_path()
75            .nodes
76            .get(route.next_idx().saturating_sub(1)..)
77            .filter(|n| n.len() >= 2)
78        {
79            gizmos.push(Gizmos::line_strip(
80                to_traverse.iter().map(|p| p.as_() + 0.5).collect(),
81                Rgb::red(),
82            ));
83        }
84    }
85    if let Some(target) = agent.chaser.last_target() {
86        gizmos.push(Gizmos::sphere(target, 0.3, Rgba::new(255, 0, 0, 200)));
87    }
88}
89
90fn gizmos_for_target(
91    gizmos: &mut Vec<Gizmos>,
92    subscription: GizmoSubscription,
93    target: Entity,
94    _range: f32,
95    data: &ReadData,
96) {
97    match subscription {
98        GizmoSubscription::PathFinding => {
99            if let Some(agent) = data.agents.get(target) {
100                pathfind_gizmos(gizmos, agent);
101            }
102        },
103    }
104}
105
106fn gizmos_for(
107    gizmos: &mut Vec<Gizmos>,
108    subscription: GizmoSubscription,
109    pos: comp::Pos,
110    range: f32,
111    data: &ReadData,
112) {
113    match subscription {
114        GizmoSubscription::PathFinding => {
115            for target in data.spatial_grid.0.in_circle_aabr(pos.0.xy(), range) {
116                gizmos_for_target(gizmos, subscription, target, range, data);
117            }
118        },
119    }
120}