1use glam::DVec3;
4
5pub(crate) const DEFAULT_LOOK_Y_MAX_ROT_SPEED: f32 = 10.0;
6pub(crate) const DEFAULT_LOOK_X_MAX_ROT_ANGLE: f32 = 40.0;
7const HEAD_STABLE_ANGLE: f32 = 15.0;
8const DELAY_UNTIL_STARTING_TO_FACE_FORWARD: i32 = 10;
9const HOW_LONG_IT_TAKES_TO_FACE_FORWARD: f32 = 10.0;
10
11fn wrap_degrees(mut degrees: f32) -> f32 {
12 degrees %= 360.0;
13 if degrees >= 180.0 {
14 degrees -= 360.0;
15 }
16 if degrees < -180.0 {
17 degrees += 360.0;
18 }
19 degrees
20}
21
22pub(crate) fn rotate_towards(from_angle: f32, to_angle: f32, max_rot: f32) -> f32 {
23 let diff = wrap_degrees(to_angle - from_angle);
24 let diff_clamped = diff.clamp(-max_rot, max_rot);
25 from_angle + diff_clamped
26}
27
28pub(crate) fn rotate_if_necessary(base_angle: f32, target_angle: f32, max_angle_diff: f32) -> f32 {
29 let delta_angle = wrap_degrees(target_angle - base_angle);
30 let delta_angle_clamped = delta_angle.clamp(-max_angle_diff, max_angle_diff);
31 target_angle - delta_angle_clamped
32}
33
34#[derive(Debug, Clone, Copy, PartialEq)]
35pub enum MoveControlOperation {
36 Wait,
37 MoveTo,
38 Strafe,
39 Jumping,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub struct MoveControl {
44 wanted_position: DVec3,
45 speed_modifier: f64,
46 strafe_forward: f32,
47 strafe_right: f32,
48 operation: MoveControlOperation,
49}
50
51impl MoveControl {
52 #[must_use]
53 pub const fn new() -> Self {
54 Self {
55 wanted_position: DVec3::ZERO,
56 speed_modifier: 0.0,
57 strafe_forward: 0.0,
58 strafe_right: 0.0,
59 operation: MoveControlOperation::Wait,
60 }
61 }
62
63 #[must_use]
64 pub const fn operation(&self) -> MoveControlOperation {
65 self.operation
66 }
67
68 #[must_use]
69 pub const fn wanted_position(&self) -> DVec3 {
70 self.wanted_position
71 }
72
73 #[must_use]
74 pub const fn speed_modifier(&self) -> f64 {
75 self.speed_modifier
76 }
77
78 #[must_use]
79 pub const fn strafe_forward(&self) -> f32 {
80 self.strafe_forward
81 }
82
83 #[must_use]
84 pub const fn strafe_right(&self) -> f32 {
85 self.strafe_right
86 }
87
88 pub fn set_wanted_position(&mut self, position: DVec3, speed_modifier: f64) {
89 self.wanted_position = position;
90 self.speed_modifier = speed_modifier;
91 if self.operation != MoveControlOperation::Jumping {
92 self.operation = MoveControlOperation::MoveTo;
93 }
94 }
95
96 pub const fn strafe(&mut self, forward: f32, right: f32) {
97 self.operation = MoveControlOperation::Strafe;
98 self.strafe_forward = forward;
99 self.strafe_right = right;
100 self.speed_modifier = 0.25;
101 }
102
103 pub const fn set_wait(&mut self) {
104 self.operation = MoveControlOperation::Wait;
105 }
106
107 pub const fn set_jumping(&mut self) {
108 self.operation = MoveControlOperation::Jumping;
109 }
110}
111
112impl Default for MoveControl {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub struct JumpControl {
120 jump: bool,
121}
122
123impl JumpControl {
124 #[must_use]
125 pub const fn new() -> Self {
126 Self { jump: false }
127 }
128
129 pub const fn jump(&mut self) {
130 self.jump = true;
131 }
132
133 pub const fn tick(&mut self) -> bool {
134 let jump = self.jump;
135 self.jump = false;
136 jump
137 }
138}
139
140impl Default for JumpControl {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146#[derive(Debug, Clone, Copy, PartialEq)]
147pub struct LookControl {
148 wanted_position: DVec3,
149 y_max_rot_speed: f32,
150 x_max_rot_angle: f32,
151 look_at_cooldown: i32,
152}
153
154impl LookControl {
155 #[must_use]
156 pub const fn new() -> Self {
157 Self {
158 wanted_position: DVec3::ZERO,
159 y_max_rot_speed: DEFAULT_LOOK_Y_MAX_ROT_SPEED,
160 x_max_rot_angle: DEFAULT_LOOK_X_MAX_ROT_ANGLE,
161 look_at_cooldown: 0,
162 }
163 }
164
165 #[must_use]
166 pub const fn wanted_position(&self) -> DVec3 {
167 self.wanted_position
168 }
169
170 #[must_use]
171 pub const fn y_max_rot_speed(&self) -> f32 {
172 self.y_max_rot_speed
173 }
174
175 #[must_use]
176 pub const fn x_max_rot_angle(&self) -> f32 {
177 self.x_max_rot_angle
178 }
179
180 #[must_use]
181 pub const fn is_looking_at_target(&self) -> bool {
182 self.look_at_cooldown > 0
183 }
184
185 pub const fn set_look_at(
186 &mut self,
187 position: DVec3,
188 y_max_rot_speed: f32,
189 x_max_rot_angle: f32,
190 ) {
191 self.wanted_position = position;
192 self.y_max_rot_speed = y_max_rot_speed;
193 self.x_max_rot_angle = x_max_rot_angle;
194 self.look_at_cooldown = 2;
195 }
196
197 pub const fn tick_cooldown(&mut self) -> bool {
198 if self.look_at_cooldown <= 0 {
199 return false;
200 }
201
202 self.look_at_cooldown -= 1;
203 true
204 }
205}
206
207impl Default for LookControl {
208 fn default() -> Self {
209 Self::new()
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq)]
214pub struct BodyRotationInput {
215 moving: bool,
216 carrying_mob_passenger: bool,
217 y_rot: f32,
218 y_body_rot: f32,
219 y_head_rot: f32,
220 max_head_y_rot: f32,
221}
222
223impl BodyRotationInput {
224 #[must_use]
225 pub const fn new(
226 moving: bool,
227 carrying_mob_passenger: bool,
228 y_rot: f32,
229 y_body_rot: f32,
230 y_head_rot: f32,
231 max_head_y_rot: f32,
232 ) -> Self {
233 Self {
234 moving,
235 carrying_mob_passenger,
236 y_rot,
237 y_body_rot,
238 y_head_rot,
239 max_head_y_rot,
240 }
241 }
242}
243
244#[derive(Debug, Clone, Copy, PartialEq)]
245pub struct BodyRotationUpdate {
246 y_body_rot: f32,
247 y_head_rot: f32,
248}
249
250impl BodyRotationUpdate {
251 #[must_use]
252 pub const fn y_body_rot(self) -> f32 {
253 self.y_body_rot
254 }
255
256 #[must_use]
257 pub const fn y_head_rot(self) -> f32 {
258 self.y_head_rot
259 }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq)]
263pub struct BodyRotationControl {
264 head_stable_time: i32,
265 last_stable_y_head_rot: f32,
266}
267
268impl BodyRotationControl {
269 #[must_use]
270 pub const fn new() -> Self {
271 Self {
272 head_stable_time: 0,
273 last_stable_y_head_rot: 0.0,
274 }
275 }
276
277 #[must_use]
278 pub const fn head_stable_time(self) -> i32 {
279 self.head_stable_time
280 }
281
282 #[must_use]
283 pub const fn last_stable_y_head_rot(self) -> f32 {
284 self.last_stable_y_head_rot
285 }
286
287 pub fn tick(&mut self, input: BodyRotationInput) -> BodyRotationUpdate {
288 let mut y_body_rot = input.y_body_rot;
289 let mut y_head_rot = input.y_head_rot;
290
291 if input.moving {
292 y_body_rot = input.y_rot;
293 y_head_rot = rotate_if_necessary(y_head_rot, y_body_rot, input.max_head_y_rot);
294 self.last_stable_y_head_rot = y_head_rot;
295 self.head_stable_time = 0;
296 } else if !input.carrying_mob_passenger {
297 if (y_head_rot - self.last_stable_y_head_rot).abs() > HEAD_STABLE_ANGLE {
298 self.head_stable_time = 0;
299 self.last_stable_y_head_rot = y_head_rot;
300 y_body_rot = rotate_if_necessary(y_body_rot, y_head_rot, input.max_head_y_rot);
301 } else {
302 self.head_stable_time += 1;
303 if self.head_stable_time > DELAY_UNTIL_STARTING_TO_FACE_FORWARD {
304 let time_since_starting_to_face_forward =
305 self.head_stable_time - DELAY_UNTIL_STARTING_TO_FACE_FORWARD;
306 let face_forward_fraction = (time_since_starting_to_face_forward as f32
307 / HOW_LONG_IT_TAKES_TO_FACE_FORWARD)
308 .clamp(0.0, 1.0);
309 let angle_remaining_until_facing_forward =
310 input.max_head_y_rot * (1.0 - face_forward_fraction);
311 y_body_rot = rotate_if_necessary(
312 y_body_rot,
313 y_head_rot,
314 angle_remaining_until_facing_forward,
315 );
316 }
317 }
318 }
319
320 BodyRotationUpdate {
321 y_body_rot,
322 y_head_rot,
323 }
324 }
325}
326
327impl Default for BodyRotationControl {
328 fn default() -> Self {
329 Self::new()
330 }
331}
332
333#[derive(Debug, Default, Clone, Copy, PartialEq)]
334pub struct MobControls {
335 pub move_control: MoveControl,
336 pub jump_control: JumpControl,
337 pub look_control: LookControl,
338 pub body_rotation_control: BodyRotationControl,
339}
340
341impl MobControls {
342 #[must_use]
343 pub const fn new() -> Self {
344 Self {
345 move_control: MoveControl::new(),
346 jump_control: JumpControl::new(),
347 look_control: LookControl::new(),
348 body_rotation_control: BodyRotationControl::new(),
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::{BodyRotationControl, BodyRotationInput};
356
357 fn assert_f32_close(left: f32, right: f32) {
358 assert!(
359 (left - right).abs() < 1.0e-6,
360 "expected {left:?} to equal {right:?}"
361 );
362 }
363
364 #[test]
365 fn body_rotation_control_faces_body_forward_while_moving() {
366 let mut control = BodyRotationControl::new();
367
368 let update = control.tick(BodyRotationInput::new(true, false, 90.0, 0.0, 200.0, 75.0));
369
370 assert_f32_close(update.y_body_rot(), 90.0);
371 assert_f32_close(update.y_head_rot(), 165.0);
372 assert_eq!(control.head_stable_time(), 0);
373 assert_f32_close(control.last_stable_y_head_rot(), 165.0);
374 }
375
376 #[test]
377 fn body_rotation_control_turns_body_when_idle_head_moves() {
378 let mut control = BodyRotationControl::new();
379
380 let update = control.tick(BodyRotationInput::new(false, false, 0.0, 0.0, 90.0, 75.0));
381
382 assert_f32_close(update.y_body_rot(), 15.0);
383 assert_f32_close(update.y_head_rot(), 90.0);
384 assert_eq!(control.head_stable_time(), 0);
385 assert_f32_close(control.last_stable_y_head_rot(), 90.0);
386 }
387
388 #[test]
389 fn body_rotation_control_waits_then_turns_body_toward_stable_head() {
390 let mut control = BodyRotationControl::new();
391 let first = control.tick(BodyRotationInput::new(false, false, 0.0, 0.0, 90.0, 75.0));
392 let mut y_body_rot = first.y_body_rot();
393
394 for _ in 0..10 {
395 y_body_rot = control
396 .tick(BodyRotationInput::new(
397 false, false, 0.0, y_body_rot, 90.0, 75.0,
398 ))
399 .y_body_rot();
400 }
401
402 assert_eq!(control.head_stable_time(), 10);
403 assert_f32_close(y_body_rot, 15.0);
404
405 let update = control.tick(BodyRotationInput::new(
406 false, false, 0.0, y_body_rot, 90.0, 75.0,
407 ));
408
409 assert_eq!(control.head_stable_time(), 11);
410 assert_f32_close(update.y_body_rot(), 22.5);
411 }
412
413 #[test]
414 fn body_rotation_control_does_not_turn_idle_body_when_carrying_mob_passenger() {
415 let mut control = BodyRotationControl::new();
416
417 let update = control.tick(BodyRotationInput::new(false, true, 0.0, 0.0, 90.0, 75.0));
418
419 assert_f32_close(update.y_body_rot(), 0.0);
420 assert_f32_close(update.y_head_rot(), 90.0);
421 assert_eq!(control.head_stable_time(), 0);
422 assert_f32_close(control.last_stable_y_head_rot(), 0.0);
423 }
424}