veloren_common/comp/
controller.rs

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