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