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 controller.inputs.sanitize();
75
76 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, 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}