veloren_common/comp/
controller.rs

1use crate::{
2    comp::{
3        BuffKind, ability,
4        dialogue::Subject,
5        inventory::{
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,
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,
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,
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 => Self::Sort,
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, Subject),
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, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
255pub enum Climb {
256    Up,
257    Down,
258    Hold,
259}
260
261#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
262pub struct ControllerInputs {
263    pub climb: Option<Climb>,
264    pub move_dir: Vec2<f32>,
265    pub move_z: f32, /* z axis (not combined with move_dir because they may have independent
266                      * limits) */
267    pub look_dir: Dir,
268    pub break_block_pos: Option<Vec3<f32>>,
269    /// Attempt to enable strafing.
270    /// Currently, setting this to false will *not* disable strafing during a
271    /// wielding character state.
272    pub strafing: bool,
273}
274
275#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
276pub struct Controller {
277    pub inputs: ControllerInputs,
278    pub queued_inputs: BTreeMap<InputKind, InputAttr>,
279    // TODO: consider SmallVec
280    pub events: Vec<ControlEvent>,
281    pub actions: Vec<ControlAction>,
282}
283
284impl ControllerInputs {
285    /// Sanitize inputs to avoid clients sending bad data.
286    pub fn sanitize(&mut self) {
287        self.move_dir = if self.move_dir.map(|e| e.is_finite()).reduce_and() {
288            self.move_dir / self.move_dir.magnitude().max(1.0)
289        } else {
290            Vec2::zero()
291        };
292        self.move_z = if self.move_z.is_finite() {
293            self.move_z.clamped(-1.0, 1.0)
294        } else {
295            0.0
296        };
297    }
298
299    /// Updates Controller inputs with new version received from the client
300    pub fn update_with_new(&mut self, new: Self) {
301        self.climb = new.climb;
302        self.move_dir = new.move_dir;
303        self.move_z = new.move_z;
304        self.look_dir = new.look_dir;
305        self.break_block_pos = new.break_block_pos;
306    }
307}
308
309impl Controller {
310    /// Sets all inputs to default
311    pub fn reset(&mut self) {
312        self.inputs = Default::default();
313        self.queued_inputs = Default::default();
314    }
315
316    pub fn clear_events(&mut self) { self.events.clear(); }
317
318    pub fn push_event(&mut self, event: ControlEvent) { self.events.push(event); }
319
320    pub fn push_utterance(&mut self, utterance: UtteranceKind) {
321        self.push_event(ControlEvent::Utterance(utterance));
322    }
323
324    pub fn push_invite_response(&mut self, invite_response: InviteResponse) {
325        self.push_event(ControlEvent::InviteResponse(invite_response));
326    }
327
328    pub fn push_initiate_invite(&mut self, uid: Uid, invite: InviteKind) {
329        self.push_event(ControlEvent::InitiateInvite(uid, invite));
330    }
331
332    pub fn push_action(&mut self, action: ControlAction) { self.actions.push(action); }
333
334    pub fn push_basic_input(&mut self, input: InputKind) {
335        self.push_action(ControlAction::basic_input(input));
336    }
337
338    pub fn push_cancel_input(&mut self, input: InputKind) {
339        self.push_action(ControlAction::CancelInput(input));
340    }
341}
342
343impl Component for Controller {
344    type Storage = specs::VecStorage<Self>;
345}