1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use common::{
    comp::{
        ability::Stance,
        agent::{Sound, SoundKind},
        Body, BuffChange, Collider, ControlEvent, Controller, Pos, Scale,
    },
    event::{self, EmitExt},
    event_emitters,
    terrain::TerrainGrid,
    uid::IdMaps,
};
use common_ecs::{Job, Origin, Phase, System};
use specs::{shred, Entities, Join, Read, ReadExpect, ReadStorage, SystemData, WriteStorage};
use vek::*;

event_emitters! {
    struct Events[EventEmitters] {
        mount: event::MountEvent,
        mount_volume: event::MountVolumeEvent,
        set_pet_stay: event::SetPetStayEvent,
        unmount: event::UnmountEvent,
        lantern: event::SetLanternEvent,
        npc_interact: event::NpcInteractEvent,
        initiate_invite: event::InitiateInviteEvent,
        invite_response: event::InviteResponseEvent,
        process_trade_action: event::ProcessTradeActionEvent,
        inventory_manip: event::InventoryManipEvent,
        group_manip: event::GroupManipEvent,
        respawn: event::RespawnEvent,
        sound: event::SoundEvent,
        change_ability: event::ChangeAbilityEvent,
        change_stance: event::ChangeStanceEvent,
        start_teleporting: event::StartTeleportingEvent,
        buff: event::BuffEvent,
    }
}

#[derive(SystemData)]
pub struct ReadData<'a> {
    entities: Entities<'a>,
    id_maps: Read<'a, IdMaps>,
    events: Events<'a>,
    terrain_grid: ReadExpect<'a, TerrainGrid>,
    positions: ReadStorage<'a, Pos>,
    bodies: ReadStorage<'a, Body>,
    scales: ReadStorage<'a, Scale>,
    colliders: ReadStorage<'a, Collider>,
}

#[derive(Default)]
pub struct Sys;

impl<'a> System<'a> for Sys {
    type SystemData = (ReadData<'a>, WriteStorage<'a, Controller>);

    const NAME: &'static str = "controller";
    const ORIGIN: Origin = Origin::Common;
    const PHASE: Phase = Phase::Create;

    fn run(_job: &mut Job<Self>, (read_data, mut controllers): Self::SystemData) {
        let mut emitters = read_data.events.get_emitters();

        for (entity, controller) in (&read_data.entities, &mut controllers).join() {
            // Sanitize inputs to avoid clients sending bad data
            controller.inputs.sanitize();

            // Process other controller events
            for event in controller.events.drain(..) {
                match event {
                    ControlEvent::Mount(mountee_uid) => {
                        if let Some(mountee_entity) = read_data.id_maps.uid_entity(mountee_uid) {
                            emitters.emit(event::MountEvent(entity, mountee_entity));
                        }
                    },
                    ControlEvent::MountVolume(volume) => {
                        if let Some(block) = volume.get_block(
                            &read_data.terrain_grid,
                            &read_data.id_maps,
                            &read_data.colliders,
                        ) {
                            if block.is_mountable() {
                                emitters.emit(event::MountVolumeEvent(entity, volume));
                            }
                        }
                    },
                    ControlEvent::SetPetStay(pet_uid, stay) => {
                        if let Some(pet_entity) = read_data.id_maps.uid_entity(pet_uid) {
                            emitters.emit(event::SetPetStayEvent(entity, pet_entity, stay));
                        }
                    },
                    ControlEvent::RemoveBuff(buff_id) => {
                        emitters.emit(event::BuffEvent {
                            entity,
                            buff_change: BuffChange::RemoveFromController(buff_id),
                        });
                    },
                    ControlEvent::Unmount => emitters.emit(event::UnmountEvent(entity)),
                    ControlEvent::EnableLantern => {
                        emitters.emit(event::SetLanternEvent(entity, true))
                    },
                    ControlEvent::DisableLantern => {
                        emitters.emit(event::SetLanternEvent(entity, false))
                    },
                    ControlEvent::Interact(npc_uid, subject) => {
                        if let Some(npc_entity) = read_data.id_maps.uid_entity(npc_uid) {
                            emitters.emit(event::NpcInteractEvent(entity, npc_entity, subject));
                        }
                    },
                    ControlEvent::InitiateInvite(inviter_uid, kind) => {
                        emitters.emit(event::InitiateInviteEvent(entity, inviter_uid, kind));
                    },
                    ControlEvent::InviteResponse(response) => {
                        emitters.emit(event::InviteResponseEvent(entity, response));
                    },
                    ControlEvent::PerformTradeAction(trade_id, action) => {
                        emitters.emit(event::ProcessTradeActionEvent(entity, trade_id, action));
                    },
                    ControlEvent::InventoryEvent(event) => {
                        emitters.emit(event::InventoryManipEvent(entity, event.into()));
                    },
                    ControlEvent::GroupManip(manip) => {
                        emitters.emit(event::GroupManipEvent(entity, manip))
                    },
                    ControlEvent::Respawn => emitters.emit(event::RespawnEvent(entity)),
                    ControlEvent::Utterance(kind) => {
                        if let (Some(pos), Some(body), scale) = (
                            read_data.positions.get(entity),
                            read_data.bodies.get(entity),
                            read_data.scales.get(entity),
                        ) {
                            let sound = Sound::new(
                                SoundKind::Utterance(kind, *body),
                                pos.0
                                    + Vec3::unit_z() * body.eye_height(scale.map_or(1.0, |s| s.0)),
                                8.0, // TODO: Come up with a better way of determining this
                                1.0,
                            );
                            emitters.emit(event::SoundEvent { sound });
                        }
                    },
                    ControlEvent::ChangeAbility {
                        slot,
                        auxiliary_key,
                        new_ability,
                    } => {
                        emitters.emit(event::ChangeAbilityEvent {
                            entity,
                            slot,
                            auxiliary_key,
                            new_ability,
                        });
                    },
                    ControlEvent::LeaveStance => {
                        emitters.emit(event::ChangeStanceEvent {
                            entity,
                            stance: Stance::None,
                        });
                    },
                    ControlEvent::ActivatePortal(portal_uid) => {
                        if let Some(portal) = read_data.id_maps.uid_entity(portal_uid) {
                            emitters.emit(event::StartTeleportingEvent { entity, portal });
                        }
                    },
                }
            }
        }
    }
}