1use crate::{
2 comp::{
3 BuffKind, ability,
4 inventory::{
5 item::tool::ToolKind,
6 slot::{EquipSlot, InvSlotId, Slot},
7 },
8 invite::{InviteKind, InviteResponse},
9 },
10 mounting::VolumePos,
11 rtsim,
12 trade::{TradeAction, TradeId},
13 uid::Uid,
14 util::Dir,
15};
16use serde::{Deserialize, Serialize};
17use specs::Component;
18use std::collections::BTreeMap;
19use vek::*;
20
21#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
22pub enum InventoryEvent {
23 Pickup(Uid),
24 Swap(InvSlotId, InvSlotId),
25 SplitSwap(InvSlotId, InvSlotId),
26 Drop(InvSlotId),
27 SplitDrop(InvSlotId),
28 Sort,
29 CraftRecipe {
30 craft_event: CraftEvent,
31 craft_sprite: Option<VolumePos>,
32 },
33 OverflowMove(usize, InvSlotId),
34 OverflowDrop(usize),
35 OverflowSplitDrop(usize),
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
39pub enum InventoryAction {
40 Swap(EquipSlot, Slot),
41 Drop(EquipSlot),
42 Use(Slot),
43 Sort,
44 Collect(Vec3<i32>),
45 ToggleSpriteLight(VolumePos, bool),
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
51pub enum InventoryManip {
52 Pickup(Uid),
53 Collect {
54 sprite_pos: Vec3<i32>,
55 required_item: Option<(InvSlotId, bool)>,
57 },
58 Use(Slot),
59 Swap(Slot, Slot),
60 SplitSwap(Slot, Slot),
61 Drop(Slot),
62 SplitDrop(Slot),
63 Sort,
64 CraftRecipe {
65 craft_event: CraftEvent,
66 craft_sprite: Option<VolumePos>,
67 },
68 SwapEquippedWeapons,
69}
70
71impl From<InventoryEvent> for InventoryManip {
72 fn from(inv_event: InventoryEvent) -> Self {
73 match inv_event {
74 InventoryEvent::Pickup(pickup) => Self::Pickup(pickup),
75 InventoryEvent::Swap(inv1, inv2) => {
76 Self::Swap(Slot::Inventory(inv1), Slot::Inventory(inv2))
77 },
78 InventoryEvent::SplitSwap(inv1, inv2) => {
79 Self::SplitSwap(Slot::Inventory(inv1), Slot::Inventory(inv2))
80 },
81 InventoryEvent::Drop(inv) => Self::Drop(Slot::Inventory(inv)),
82 InventoryEvent::SplitDrop(inv) => Self::SplitDrop(Slot::Inventory(inv)),
83 InventoryEvent::Sort => Self::Sort,
84 InventoryEvent::CraftRecipe {
85 craft_event,
86 craft_sprite,
87 } => Self::CraftRecipe {
88 craft_event,
89 craft_sprite,
90 },
91 InventoryEvent::OverflowMove(o, inv) => {
92 Self::Swap(Slot::Overflow(o), Slot::Inventory(inv))
93 },
94 InventoryEvent::OverflowDrop(o) => Self::Drop(Slot::Overflow(o)),
95 InventoryEvent::OverflowSplitDrop(o) => Self::SplitDrop(Slot::Overflow(o)),
96 }
97 }
98}
99
100#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
101pub enum CraftEvent {
102 Simple {
103 recipe: String,
104 slots: Vec<(u32, InvSlotId)>,
105 amount: u32,
106 },
107 Salvage(InvSlotId),
108 ModularWeapon {
110 primary_component: InvSlotId,
111 secondary_component: InvSlotId,
112 },
113 ModularWeaponPrimaryComponent {
115 toolkind: ToolKind,
116 material: InvSlotId,
117 modifier: Option<InvSlotId>,
118 slots: Vec<(u32, InvSlotId)>,
119 },
120 Repair {
121 item: Slot,
122 slots: Vec<(u32, InvSlotId)>,
123 },
124}
125
126#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
127pub enum GroupManip {
128 Leave,
129 Kick(Uid),
130 AssignLeader(Uid),
131}
132
133#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, strum::EnumString)]
134pub enum UtteranceKind {
135 Calm,
136 Angry,
137 Surprised,
138 Hurt,
139 Greeting,
140 Scream,
141 Ambush,
142 }
146
147#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
148pub enum ControlEvent {
149 EnableLantern,
151 DisableLantern,
152 Interact(Uid),
153 InitiateInvite(Uid, InviteKind),
154 InviteResponse(InviteResponse),
155 PerformTradeAction(TradeId, TradeAction),
156 Mount(Uid),
157 MountVolume(VolumePos),
158 Unmount,
159 SetPetStay(Uid, bool),
160 InventoryEvent(InventoryEvent),
161 GroupManip(GroupManip),
162 RemoveBuff(BuffKind),
163 LeaveStance,
164 GiveUp,
165 Respawn,
166 Utterance(UtteranceKind),
167 ChangeAbility {
168 slot: usize,
169 auxiliary_key: ability::AuxiliaryKey,
170 new_ability: ability::AuxiliaryAbility,
171 },
172 ActivatePortal(Uid),
173 InteractWith {
174 target: Uid,
175 kind: crate::interaction::InteractionKind,
176 },
177 Dialogue(Uid, rtsim::Dialogue),
178}
179
180#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
181pub enum ControlAction {
182 SwapEquippedWeapons,
183 InventoryAction(InventoryAction),
184 Wield,
185 GlideWield,
186 Unwield,
187 Sit,
188 Crawl,
189 Dance,
190 Sneak,
191 Stand,
192 Talk,
193 StartInput {
194 input: InputKind,
195 target_entity: Option<Uid>,
196 select_pos: Option<Vec3<f32>>,
198 },
199 CancelInput(InputKind),
200}
201
202impl ControlAction {
203 pub fn basic_input(input: InputKind) -> Self {
204 ControlAction::StartInput {
205 input,
206 target_entity: None,
207 select_pos: None,
208 }
209 }
210}
211
212#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Ord, PartialOrd)]
213#[repr(u32)]
214pub enum InputKind {
215 Primary = 0,
216 Secondary = 1,
217 Block = 2,
218 Ability(usize) = 3,
219 Roll = 4,
220 Jump = 5,
221 Fly = 6,
222}
223
224impl InputKind {
225 pub fn is_ability(self) -> bool {
226 matches!(
227 self,
228 Self::Primary | Self::Secondary | Self::Ability(_) | Self::Block
229 )
230 }
231}
232
233impl From<InputKind> for Option<ability::AbilityInput> {
234 fn from(input: InputKind) -> Option<ability::AbilityInput> {
235 use ability::AbilityInput;
236 match input {
237 InputKind::Block => Some(AbilityInput::Guard),
238 InputKind::Primary => Some(AbilityInput::Primary),
239 InputKind::Secondary => Some(AbilityInput::Secondary),
240 InputKind::Roll => Some(AbilityInput::Movement),
241 InputKind::Ability(index) => Some(AbilityInput::Auxiliary(index)),
242 InputKind::Jump | InputKind::Fly => None,
243 }
244 }
245}
246
247#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
248pub struct InputAttr {
249 pub select_pos: Option<Vec3<f32>>,
250 pub target_entity: Option<Uid>,
251}
252
253#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
254pub struct ControllerInputs {
255 pub move_dir: Vec2<f32>,
256 pub move_z: f32, pub look_dir: Dir,
259 pub break_block_pos: Option<Vec3<f32>>,
260 pub strafing: bool,
264}
265
266#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
267pub struct Controller {
268 pub inputs: ControllerInputs,
269 pub queued_inputs: BTreeMap<InputKind, InputAttr>,
270 pub events: Vec<ControlEvent>,
272 pub actions: Vec<ControlAction>,
273}
274
275impl ControllerInputs {
276 pub fn sanitize(&mut self) {
278 self.move_dir = if self.move_dir.map(|e| e.is_finite()).reduce_and() {
279 self.move_dir / self.move_dir.magnitude().max(1.0)
280 } else {
281 Vec2::zero()
282 };
283 self.move_z = if self.move_z.is_finite() {
284 self.move_z.clamped(-1.0, 1.0)
285 } else {
286 0.0
287 };
288 }
289
290 pub fn update_with_new(&mut self, new: Self) {
292 self.move_dir = new.move_dir;
293 self.move_z = new.move_z;
294 self.look_dir = new.look_dir;
295 self.break_block_pos = new.break_block_pos;
296 }
297}
298
299impl Controller {
300 pub fn reset(&mut self) {
302 self.inputs = Default::default();
303 self.queued_inputs = Default::default();
304 }
305
306 pub fn clear_events(&mut self) { self.events.clear(); }
307
308 pub fn push_event(&mut self, event: ControlEvent) { self.events.push(event); }
309
310 pub fn push_utterance(&mut self, utterance: UtteranceKind) {
311 self.push_event(ControlEvent::Utterance(utterance));
312 }
313
314 pub fn push_invite_response(&mut self, invite_response: InviteResponse) {
315 self.push_event(ControlEvent::InviteResponse(invite_response));
316 }
317
318 pub fn push_initiate_invite(&mut self, uid: Uid, invite: InviteKind) {
319 self.push_event(ControlEvent::InitiateInvite(uid, invite));
320 }
321
322 pub fn push_action(&mut self, action: ControlAction) { self.actions.push(action); }
323
324 pub fn push_basic_input(&mut self, input: InputKind) {
325 self.push_action(ControlAction::basic_input(input));
326 }
327
328 pub fn push_cancel_input(&mut self, input: InputKind) {
329 self.push_action(ControlAction::CancelInput(input));
330 }
331}
332
333impl Component for Controller {
334 type Storage = specs::VecStorage<Self>;
335}