veloren_server/sys/
object.rs

1use common::{
2    CachedSpatialGrid,
3    comp::{Body, Object, Pos, Teleporting, object},
4    consts::TELEPORTER_RADIUS,
5    event::{ChangeBodyEvent, DeleteEvent, EmitExt, EventBus, ExplosionEvent, ShootEvent},
6    event_emitters,
7    outcome::Outcome,
8    resources::{DeltaTime, Time},
9};
10use common_ecs::{Job, Origin, Phase, System};
11use specs::{Entities, Join, LendJoin, Read, ReadStorage};
12
13event_emitters! {
14    struct Events[Emitters] {
15        delete: DeleteEvent,
16        explosion: ExplosionEvent,
17        shoot: ShootEvent,
18        change_body: ChangeBodyEvent,
19    }
20}
21
22/// This system is responsible for handling misc object behaviours
23#[derive(Default)]
24pub struct Sys;
25impl<'a> System<'a> for Sys {
26    type SystemData = (
27        Entities<'a>,
28        Events<'a>,
29        Read<'a, DeltaTime>,
30        Read<'a, Time>,
31        Read<'a, EventBus<Outcome>>,
32        Read<'a, CachedSpatialGrid>,
33        ReadStorage<'a, Pos>,
34        ReadStorage<'a, Object>,
35        ReadStorage<'a, Body>,
36        ReadStorage<'a, Teleporting>,
37    );
38
39    const NAME: &'static str = "object";
40    const ORIGIN: Origin = Origin::Server;
41    const PHASE: Phase = Phase::Create;
42
43    fn run(
44        _job: &mut Job<Self>,
45        (
46            entities,
47            events,
48            _dt,
49            time,
50            outcome_bus,
51            spatial_grid,
52            positions,
53            objects,
54            bodies,
55            teleporting,
56        ): Self::SystemData,
57    ) {
58        let mut emitters = events.get_emitters();
59
60        // Objects
61        for (entity, pos, object, body) in (&entities, &positions, &objects, bodies.maybe()).join()
62        {
63            match object {
64                Object::DeleteAfter {
65                    spawned_at,
66                    timeout,
67                } => {
68                    if (time.0 - spawned_at.0).max(0.0) > timeout.as_secs_f64() {
69                        emitters.emit(DeleteEvent(entity));
70                    }
71                },
72                Object::Portal { .. } => {
73                    let is_active = spatial_grid
74                        .0
75                        .in_circle_aabr(pos.0.xy(), TELEPORTER_RADIUS)
76                        .any(|entity| {
77                            (&positions, &teleporting)
78                                .lend_join()
79                                .get(entity, &entities)
80                                .is_some_and(|(teleporter_pos, _)| {
81                                    pos.0.distance_squared(teleporter_pos.0)
82                                        <= TELEPORTER_RADIUS.powi(2)
83                                })
84                        });
85
86                    if body.is_some_and(|body| {
87                        (*body == Body::Object(object::Body::PortalActive)) != is_active
88                    }) {
89                        emitters.emit(ChangeBodyEvent {
90                            entity,
91                            new_body: Body::Object(if is_active {
92                                outcome_bus.emit_now(Outcome::PortalActivated { pos: pos.0 });
93                                object::Body::PortalActive
94                            } else {
95                                object::Body::Portal
96                            }),
97                        });
98                    }
99                },
100            }
101        }
102    }
103}