steel_core/entity/ai/goal/
random_stroll.rs1use glam::DVec3;
2
3use super::random_pos::default_random_pos;
4use super::reduced_tick_delay;
5use super::selector::{Goal, GoalControls};
6use crate::entity::PathfinderMob;
7
8const RANDOM_STROLL_DEFAULT_INTERVAL: i32 = 120;
9
10pub struct RandomStrollGoal {
11 wanted_position: Option<DVec3>,
12 speed_modifier: f64,
13 interval: i32,
14 force_trigger: bool,
15 check_no_action_time: bool,
16}
17
18impl RandomStrollGoal {
19 #[must_use]
20 pub const fn new(speed_modifier: f64) -> Self {
21 Self::with_interval(speed_modifier, RANDOM_STROLL_DEFAULT_INTERVAL)
22 }
23
24 #[must_use]
25 pub const fn with_interval(speed_modifier: f64, interval: i32) -> Self {
26 Self::with_interval_and_no_action_time_check(speed_modifier, interval, true)
27 }
28
29 #[must_use]
30 pub const fn with_interval_and_no_action_time_check(
31 speed_modifier: f64,
32 interval: i32,
33 check_no_action_time: bool,
34 ) -> Self {
35 Self {
36 wanted_position: None,
37 speed_modifier,
38 interval,
39 force_trigger: false,
40 check_no_action_time,
41 }
42 }
43
44 pub const fn trigger(&mut self) {
45 self.force_trigger = true;
46 }
47
48 pub const fn set_interval(&mut self, interval: i32) {
49 self.interval = interval;
50 }
51
52 pub(super) fn can_use_with_position(
53 &mut self,
54 mob: &dyn PathfinderMob,
55 mut get_position: impl FnMut(&dyn PathfinderMob) -> Option<DVec3>,
56 ) -> bool {
57 if mob.has_controlling_passenger() {
58 return false;
59 }
60
61 if !self.force_trigger {
62 if self.check_no_action_time && mob.no_action_time() >= 100 {
63 return false;
64 }
65
66 let should_skip = rand::random_range(0..reduced_tick_delay(self.interval)) != 0;
67 if should_skip {
68 return false;
69 }
70 }
71
72 let Some(position) = get_position(mob) else {
73 return false;
74 };
75
76 self.wanted_position = Some(position);
77 self.force_trigger = false;
78 true
79 }
80}
81
82impl Goal for RandomStrollGoal {
83 fn controls(&self) -> GoalControls {
84 GoalControls::MOVE
85 }
86
87 fn can_use(&mut self, mob: &dyn PathfinderMob) -> bool {
88 self.can_use_with_position(mob, |mob| default_random_pos(mob, 10, 7))
89 }
90
91 fn can_continue_to_use(&mut self, mob: &dyn PathfinderMob) -> bool {
92 !mob.mob_base().navigation().lock().is_done() && !mob.has_controlling_passenger()
93 }
94
95 fn start(&mut self, mob: &dyn PathfinderMob) {
96 if let Some(wanted_position) = self.wanted_position {
97 mob.move_to_pos(wanted_position, self.speed_modifier);
98 }
99 }
100
101 fn stop(&mut self, mob: &dyn PathfinderMob) {
102 mob.mob_base().navigation().lock().stop();
103 }
104}