veloren_common/comp/
controller.rs

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