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