veloren_common_systems/
controller.rs

1use common::{
2    comp::{
3        Body, BuffChange, Collider, ControlEvent, Controller, Health, Pos, Scale,
4        UnresolvedChatMsg,
5        ability::Stance,
6        agent::{Sound, SoundKind},
7    },
8    event::{self, ChatEvent, EmitExt},
9    event_emitters,
10    interaction::Interaction,
11    terrain::TerrainGrid,
12    uid::{IdMaps, Uid},
13};
14use common_ecs::{Job, Origin, Phase, System};
15use specs::{Entities, LendJoin, Read, ReadExpect, ReadStorage, SystemData, WriteStorage, shred};
16use tracing::warn;
17use vek::*;
18
19event_emitters! {
20    struct Events[EventEmitters] {
21        mount: event::MountEvent,
22        set_pet_stay: event::SetPetStayEvent,
23        lantern: event::SetLanternEvent,
24        npc_interact: event::NpcInteractEvent,
25        dialogue: event::DialogueEvent,
26        initiate_invite: event::InitiateInviteEvent,
27        invite_response: event::InviteResponseEvent,
28        process_trade_action: event::ProcessTradeActionEvent,
29        inventory_manip: event::InventoryManipEvent,
30        group_manip: event::GroupManipEvent,
31        respawn: event::RespawnEvent,
32        sound: event::SoundEvent,
33        change_ability: event::ChangeAbilityEvent,
34        change_stance: event::ChangeStanceEvent,
35        start_teleporting: event::StartTeleportingEvent,
36        buff: event::BuffEvent,
37        start_interaction: event::StartInteractionEvent,
38        kill: event::KillEvent,
39        chat: event::ChatEvent,
40    }
41}
42
43#[derive(SystemData)]
44pub struct ReadData<'a> {
45    entities: Entities<'a>,
46    id_maps: Read<'a, IdMaps>,
47    events: Events<'a>,
48    terrain_grid: ReadExpect<'a, TerrainGrid>,
49    positions: ReadStorage<'a, Pos>,
50    bodies: ReadStorage<'a, Body>,
51    scales: ReadStorage<'a, Scale>,
52    colliders: ReadStorage<'a, Collider>,
53    uids: ReadStorage<'a, Uid>,
54    healths: ReadStorage<'a, Health>,
55}
56
57#[derive(Default)]
58pub struct Sys;
59
60impl<'a> System<'a> for Sys {
61    type SystemData = (ReadData<'a>, WriteStorage<'a, Controller>);
62
63    const NAME: &'static str = "controller";
64    const ORIGIN: Origin = Origin::Common;
65    const PHASE: Phase = Phase::Create;
66
67    fn run(_job: &mut Job<Self>, (read_data, mut controllers): Self::SystemData) {
68        let mut emitters = read_data.events.get_emitters();
69
70        (&read_data.entities, &read_data.uids, &mut controllers)
71            .lend_join()
72            .for_each(|(entity, uid, controller)| {
73                // Sanitize inputs to avoid clients sending bad data
74                controller.inputs.sanitize();
75
76                // Process other controller events
77                for event in controller.events.drain(..) {
78                    match event {
79                        ControlEvent::Mount(mountee_uid) => {
80                            if let Some(mountee_entity) = read_data.id_maps.uid_entity(mountee_uid)
81                            {
82                                emitters
83                                    .emit(event::MountEvent::MountEntity(entity, mountee_entity));
84                            }
85                        },
86                        ControlEvent::MountVolume(volume) => {
87                            if let Some(block) = volume.get_block(
88                                &read_data.terrain_grid,
89                                &read_data.id_maps,
90                                &read_data.colliders,
91                            ) {
92                                if block.is_mountable() {
93                                    emitters.emit(event::MountEvent::MountVolume(entity, volume));
94                                }
95                            }
96                        },
97                        ControlEvent::SetPetStay(pet_uid, stay) => {
98                            if let Some(pet_entity) = read_data.id_maps.uid_entity(pet_uid) {
99                                emitters.emit(event::SetPetStayEvent(entity, pet_entity, stay));
100                            }
101                        },
102                        ControlEvent::RemoveBuff(buff_id) => {
103                            emitters.emit(event::BuffEvent {
104                                entity,
105                                buff_change: BuffChange::RemoveFromController(buff_id),
106                            });
107                        },
108                        ControlEvent::Unmount => emitters.emit(event::MountEvent::Unmount(entity)),
109                        ControlEvent::EnableLantern => {
110                            emitters.emit(event::SetLanternEvent(entity, true))
111                        },
112                        ControlEvent::DisableLantern => {
113                            emitters.emit(event::SetLanternEvent(entity, false))
114                        },
115                        ControlEvent::Interact(npc_uid, subject) => {
116                            if let Some(npc_entity) = read_data.id_maps.uid_entity(npc_uid) {
117                                emitters.emit(event::NpcInteractEvent(entity, npc_entity, subject));
118                            }
119                        },
120                        ControlEvent::InitiateInvite(inviter_uid, kind) => {
121                            emitters.emit(event::InitiateInviteEvent(entity, inviter_uid, kind));
122                        },
123                        ControlEvent::InviteResponse(response) => {
124                            emitters.emit(event::InviteResponseEvent(entity, response));
125                        },
126                        ControlEvent::PerformTradeAction(trade_id, action) => {
127                            emitters.emit(event::ProcessTradeActionEvent(entity, trade_id, action));
128                        },
129                        ControlEvent::InventoryEvent(event) => {
130                            emitters.emit(event::InventoryManipEvent(entity, event.into()));
131                        },
132                        ControlEvent::GroupManip(manip) => {
133                            emitters.emit(event::GroupManipEvent(entity, manip))
134                        },
135                        ControlEvent::GiveUp => {
136                            if read_data
137                                .healths
138                                .get(entity)
139                                .is_some_and(|h| h.has_consumed_death_protection())
140                            {
141                                emitters.emit(event::KillEvent { entity });
142                            }
143                        },
144                        ControlEvent::Respawn => {
145                            if read_data.healths.get(entity).is_some_and(|h| h.is_dead) {
146                                emitters.emit(event::RespawnEvent(entity));
147                            }
148                        },
149                        ControlEvent::Utterance(kind) => {
150                            if let (Some(pos), Some(body), scale) = (
151                                read_data.positions.get(entity),
152                                read_data.bodies.get(entity),
153                                read_data.scales.get(entity),
154                            ) {
155                                let sound = Sound::new(
156                                    SoundKind::Utterance(kind, *body),
157                                    pos.0
158                                        + Vec3::unit_z()
159                                            * body.eye_height(scale.map_or(1.0, |s| s.0)),
160                                    8.0, // TODO: Come up with a better way of determining this
161                                    1.0,
162                                );
163                                emitters.emit(event::SoundEvent { sound });
164                            }
165                        },
166                        ControlEvent::ChangeAbility {
167                            slot,
168                            auxiliary_key,
169                            new_ability,
170                        } => {
171                            emitters.emit(event::ChangeAbilityEvent {
172                                entity,
173                                slot,
174                                auxiliary_key,
175                                new_ability,
176                            });
177                        },
178                        ControlEvent::LeaveStance => {
179                            emitters.emit(event::ChangeStanceEvent {
180                                entity,
181                                stance: Stance::None,
182                            });
183                        },
184                        ControlEvent::ActivatePortal(portal_uid) => {
185                            if let Some(portal) = read_data.id_maps.uid_entity(portal_uid) {
186                                emitters.emit(event::StartTeleportingEvent { entity, portal });
187                            }
188                        },
189                        ControlEvent::InteractWith { target, kind } => {
190                            if let Some(uid) = read_data.uids.get(entity) {
191                                emitters.emit(event::StartInteractionEvent(Interaction {
192                                    interactor: *uid,
193                                    target,
194                                    kind,
195                                }));
196                            }
197                        },
198                        ControlEvent::Dialogue(target, dialogue) => {
199                            if let Some(msg) = dialogue.message().cloned() {
200                                emitters.emit(ChatEvent {
201                                    msg: UnresolvedChatMsg::npc(*uid, msg),
202                                    from_client: false,
203                                });
204                            }
205                            if let Some(target) = read_data.id_maps.uid_entity(target) {
206                                emitters.emit(event::DialogueEvent(
207                                    entity,
208                                    target,
209                                    dialogue.clone(),
210                                ));
211                            } else {
212                                warn!("Control event Dialogue sent to non-existent target entity");
213                            }
214                        },
215                    }
216                }
217            });
218    }
219}