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(Slot),
123}
124
125#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
126pub enum GroupManip {
127    Leave,
128    Kick(Uid),
129    AssignLeader(Uid),
130}
131
132#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, strum::EnumString)]
133pub enum UtteranceKind {
134    Calm,
135    Angry,
136    Surprised,
137    Hurt,
138    Greeting,
139    Scream,
140    Ambush,
141    /* Death,
142     * TODO: Wait for more post-death features (i.e. animations) before implementing death
143     * sounds */
144}
145
146#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
147pub enum ControlEvent {
148    //ToggleLantern,
149    EnableLantern,
150    DisableLantern,
151    Interact(Uid),
152    InitiateInvite(Uid, InviteKind),
153    InviteResponse(InviteResponse),
154    PerformTradeAction(TradeId, TradeAction),
155    Mount(Uid),
156    MountVolume(VolumePos),
157    Unmount,
158    SetPetStay(Uid, bool),
159    InventoryEvent(InventoryEvent),
160    GroupManip(GroupManip),
161    RemoveBuff(BuffKind),
162    LeaveStance,
163    GiveUp,
164    Respawn,
165    Utterance(UtteranceKind),
166    ChangeAbility {
167        slot: usize,
168        auxiliary_key: ability::AuxiliaryKey,
169        new_ability: ability::AuxiliaryAbility,
170    },
171    ActivatePortal(Uid),
172    InteractWith {
173        target: Uid,
174        kind: crate::interaction::InteractionKind,
175    },
176    Dialogue(Uid, rtsim::Dialogue),
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
180pub enum ControlAction {
181    SwapEquippedWeapons,
182    InventoryAction(InventoryAction),
183    Wield,
184    GlideWield,
185    Unwield,
186    Sit,
187    Crawl,
188    Dance,
189    Sneak,
190    Stand,
191    Talk(Option<Uid>),
192    StartInput {
193        input: InputKind,
194        target_entity: Option<Uid>,
195        // Some inputs need a selected position, such as mining
196        select_pos: Option<Vec3<f32>>,
197    },
198    CancelInput {
199        input: InputKind,
200    },
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    WallJump = 7,
224}
225
226impl InputKind {
227    pub fn is_ability(self) -> bool {
228        matches!(
229            self,
230            Self::Primary | Self::Secondary | Self::Ability(_) | Self::Block
231        )
232    }
233}
234
235impl From<InputKind> for Option<ability::AbilityInput> {
236    fn from(input: InputKind) -> Option<ability::AbilityInput> {
237        use ability::AbilityInput;
238        match input {
239            InputKind::Block => Some(AbilityInput::Guard),
240            InputKind::Primary => Some(AbilityInput::Primary),
241            InputKind::Secondary => Some(AbilityInput::Secondary),
242            InputKind::Roll => Some(AbilityInput::Movement),
243            InputKind::Ability(index) => Some(AbilityInput::Auxiliary(index)),
244            InputKind::Jump | InputKind::WallJump | InputKind::Fly => None,
245        }
246    }
247}
248
249#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
250pub struct InputAttr {
251    pub select_pos: Option<Vec3<f32>>,
252    pub target_entity: Option<Uid>,
253}
254
255#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
256pub struct ControllerInputs {
257    pub move_dir: Vec2<f32>,
258    pub move_z: f32, /* z axis (not combined with move_dir because they may have independent
259                      * limits) */
260    pub look_dir: Dir,
261    pub break_block_pos: Option<Vec3<f32>>,
262    /// Attempt to enable strafing.
263    /// Currently, setting this to false will *not* disable strafing during a
264    /// wielding character state.
265    pub strafing: bool,
266}
267
268#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
269pub struct Controller {
270    pub inputs: ControllerInputs,
271    pub queued_inputs: BTreeMap<InputKind, InputAttr>,
272    // TODO: consider SmallVec
273    pub events: Vec<ControlEvent>,
274    pub actions: Vec<ControlAction>,
275}
276
277impl ControllerInputs {
278    /// Sanitize inputs to avoid clients sending bad data.
279    pub fn sanitize(&mut self) {
280        self.move_dir = if self.move_dir.map(|e| e.is_finite()).reduce_and() {
281            self.move_dir / self.move_dir.magnitude().max(1.0)
282        } else {
283            Vec2::zero()
284        };
285        self.move_z = if self.move_z.is_finite() {
286            self.move_z.clamped(-1.0, 1.0)
287        } else {
288            0.0
289        };
290    }
291
292    /// Updates Controller inputs with new version received from the client
293    pub fn update_with_new(&mut self, new: Self) {
294        self.move_dir = new.move_dir;
295        self.move_z = new.move_z;
296        self.look_dir = new.look_dir;
297        self.break_block_pos = new.break_block_pos;
298    }
299}
300
301impl Controller {
302    /// Sets all inputs to default
303    pub fn reset(&mut self) {
304        self.inputs = Default::default();
305        self.queued_inputs = Default::default();
306    }
307
308    pub fn clear_events(&mut self) { self.events.clear(); }
309
310    pub fn push_event(&mut self, event: ControlEvent) { self.events.push(event); }
311
312    pub fn push_utterance(&mut self, utterance: UtteranceKind) {
313        self.push_event(ControlEvent::Utterance(utterance));
314    }
315
316    pub fn push_invite_response(&mut self, invite_response: InviteResponse) {
317        self.push_event(ControlEvent::InviteResponse(invite_response));
318    }
319
320    pub fn push_initiate_invite(&mut self, uid: Uid, invite: InviteKind) {
321        self.push_event(ControlEvent::InitiateInvite(uid, invite));
322    }
323
324    pub fn push_action(&mut self, action: ControlAction) { self.actions.push(action); }
325
326    pub fn push_basic_input(&mut self, input: InputKind) {
327        self.push_action(ControlAction::basic_input(input));
328    }
329
330    pub fn push_cancel_input(&mut self, input: InputKind) {
331        self.push_action(ControlAction::CancelInput { input });
332    }
333}
334
335impl Component for Controller {
336    type Storage = specs::VecStorage<Self>;
337}