steel_core/player/movement/
input.rs1use glam::DVec3;
4
5const FLAG_FORWARD: u8 = 0x01;
6const FLAG_BACKWARD: u8 = 0x02;
7const FLAG_LEFT: u8 = 0x04;
8const FLAG_RIGHT: u8 = 0x08;
9const FLAG_JUMP: u8 = 0x10;
10const FLAG_SHIFT: u8 = 0x20;
11const FLAG_SPRINT: u8 = 0x40;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct PlayerInput {
16 forward: bool,
17 backward: bool,
18 left: bool,
19 right: bool,
20 jump: bool,
21 shift: bool,
22 sprint: bool,
23}
24
25impl PlayerInput {
26 pub const EMPTY: Self = Self::from_flags(0);
28
29 #[must_use]
31 pub const fn from_flags(flags: u8) -> Self {
32 Self {
33 forward: flags & FLAG_FORWARD != 0,
34 backward: flags & FLAG_BACKWARD != 0,
35 left: flags & FLAG_LEFT != 0,
36 right: flags & FLAG_RIGHT != 0,
37 jump: flags & FLAG_JUMP != 0,
38 shift: flags & FLAG_SHIFT != 0,
39 sprint: flags & FLAG_SPRINT != 0,
40 }
41 }
42
43 #[must_use]
45 pub const fn flags(self) -> u8 {
46 let mut flags = 0;
47 flags |= if self.forward { FLAG_FORWARD } else { 0 };
48 flags |= if self.backward { FLAG_BACKWARD } else { 0 };
49 flags |= if self.left { FLAG_LEFT } else { 0 };
50 flags |= if self.right { FLAG_RIGHT } else { 0 };
51 flags |= if self.jump { FLAG_JUMP } else { 0 };
52 flags |= if self.shift { FLAG_SHIFT } else { 0 };
53 flags |= if self.sprint { FLAG_SPRINT } else { 0 };
54 flags
55 }
56
57 #[must_use]
59 pub const fn forward(self) -> bool {
60 self.forward
61 }
62
63 #[must_use]
65 pub const fn backward(self) -> bool {
66 self.backward
67 }
68
69 #[must_use]
71 pub const fn left(self) -> bool {
72 self.left
73 }
74
75 #[must_use]
77 pub const fn right(self) -> bool {
78 self.right
79 }
80
81 #[must_use]
83 pub const fn jump(self) -> bool {
84 self.jump
85 }
86
87 #[must_use]
89 pub const fn shift(self) -> bool {
90 self.shift
91 }
92
93 #[must_use]
95 pub const fn sprint(self) -> bool {
96 self.sprint
97 }
98
99 #[must_use]
101 pub const fn left_intent(self) -> f32 {
102 if self.left == self.right {
103 0.0
104 } else if self.left {
105 1.0
106 } else {
107 -1.0
108 }
109 }
110
111 #[must_use]
113 pub const fn forward_intent(self) -> f32 {
114 if self.forward == self.backward {
115 0.0
116 } else if self.forward {
117 1.0
118 } else {
119 -1.0
120 }
121 }
122
123 #[must_use]
125 pub fn movement_input(self) -> DVec3 {
126 DVec3::new(
127 f64::from(self.left_intent()),
128 0.0,
129 f64::from(self.forward_intent()),
130 )
131 }
132}
133
134impl Default for PlayerInput {
135 fn default() -> Self {
136 Self::EMPTY
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::PlayerInput;
143 use glam::DVec3;
144
145 #[test]
146 fn movement_input_matches_vanilla_intent_axes() {
147 assert_eq!(PlayerInput::EMPTY.movement_input(), DVec3::ZERO);
148 assert_eq!(
149 PlayerInput::from_flags(0x01 | 0x08).movement_input(),
150 DVec3::new(-1.0, 0.0, 1.0)
151 );
152 assert_eq!(
153 PlayerInput::from_flags(0x01 | 0x02 | 0x04 | 0x08).movement_input(),
154 DVec3::ZERO
155 );
156 }
157
158 #[test]
159 fn input_flags_round_trip() {
160 let flags = 0x01 | 0x10 | 0x20 | 0x40;
161 let input = PlayerInput::from_flags(flags);
162
163 assert!(input.forward());
164 assert!(!input.backward());
165 assert!(input.jump());
166 assert!(input.shift());
167 assert!(input.sprint());
168 assert_eq!(input.flags(), flags);
169 }
170}