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,
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(InputKind),
201}
202
203impl ControlAction {
204    pub fn basic_input(input: InputKind) -> Self {
205        ControlAction::StartInput {
206            input,
207            target_entity: None,
208            select_pos: None,
209        }
210    }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Ord, PartialOrd)]
214#[repr(u32)]
215pub enum InputKind {
216    Primary = 0,
217    Secondary = 1,
218    Block = 2,
219    Ability(usize) = 3,
220    Roll = 4,
221    Jump = 5,
222    Fly = 6,
223}
224
225impl InputKind {
226    pub fn is_ability(self) -> bool {
227        matches!(
228            self,
229            Self::Primary | Self::Secondary | Self::Ability(_) | Self::Block
230        )
231    }
232}
233
234impl From<InputKind> for Option<ability::AbilityInput> {
235    fn from(input: InputKind) -> Option<ability::AbilityInput> {
236        use ability::AbilityInput;
237        match input {
238            InputKind::Block => Some(AbilityInput::Guard),
239            InputKind::Primary => Some(AbilityInput::Primary),
240            InputKind::Secondary => Some(AbilityInput::Secondary),
241            InputKind::Roll => Some(AbilityInput::Movement),
242            InputKind::Ability(index) => Some(AbilityInput::Auxiliary(index)),
243            InputKind::Jump | InputKind::Fly => None,
244        }
245    }
246}
247
248#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
249pub struct InputAttr {
250    pub select_pos: Option<Vec3<f32>>,
251    pub target_entity: Option<Uid>,
252}
253
254#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
255pub struct ControllerInputs {
256    pub move_dir: Vec2<f32>,
257    pub move_z: f32, /* z axis (not combined with move_dir because they may have independent
258                      * limits) */
259    pub look_dir: Dir,
260    pub break_block_pos: Option<Vec3<f32>>,
261    /// Attempt to enable strafing.
262    /// Currently, setting this to false will *not* disable strafing during a
263    /// wielding character state.
264    pub strafing: bool,
265}
266
267#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
268pub struct Controller {
269    pub inputs: ControllerInputs,
270    pub queued_inputs: BTreeMap<InputKind, InputAttr>,
271    // TODO: consider SmallVec
272    pub events: Vec<ControlEvent>,
273    pub actions: Vec<ControlAction>,
274}
275
276impl ControllerInputs {
277    /// Sanitize inputs to avoid clients sending bad data.
278    pub fn sanitize(&mut self) {
279        self.move_dir = if self.move_dir.map(|e| e.is_finite()).reduce_and() {
280            self.move_dir / self.move_dir.magnitude().max(1.0)
281        } else {
282            Vec2::zero()
283        };
284        self.move_z = if self.move_z.is_finite() {
285            self.move_z.clamped(-1.0, 1.0)
286        } else {
287            0.0
288        };
289    }
290
291    /// Updates Controller inputs with new version received from the client
292    pub fn update_with_new(&mut self, new: Self) {
293        self.move_dir = new.move_dir;
294        self.move_z = new.move_z;
295        self.look_dir = new.look_dir;
296        self.break_block_pos = new.break_block_pos;
297    }
298}
299
300impl Controller {
301    /// Sets all inputs to default
302    pub fn reset(&mut self) {
303        self.inputs = Default::default();
304        self.queued_inputs = Default::default();
305    }
306
307    pub fn clear_events(&mut self) { self.events.clear(); }
308
309    pub fn push_event(&mut self, event: ControlEvent) { self.events.push(event); }
310
311    pub fn push_utterance(&mut self, utterance: UtteranceKind) {
312        self.push_event(ControlEvent::Utterance(utterance));
313    }
314
315    pub fn push_invite_response(&mut self, invite_response: InviteResponse) {
316        self.push_event(ControlEvent::InviteResponse(invite_response));
317    }
318
319    pub fn push_initiate_invite(&mut self, uid: Uid, invite: InviteKind) {
320        self.push_event(ControlEvent::InitiateInvite(uid, invite));
321    }
322
323    pub fn push_action(&mut self, action: ControlAction) { self.actions.push(action); }
324
325    pub fn push_basic_input(&mut self, input: InputKind) {
326        self.push_action(ControlAction::basic_input(input));
327    }
328
329    pub fn push_cancel_input(&mut self, input: InputKind) {
330        self.push_action(ControlAction::CancelInput(input));
331    }
332}
333
334impl Component for Controller {
335    type Storage = specs::VecStorage<Self>;
336}