behaviortree_core/behaviors/
fallbacks.rs1use alloc::boxed::Box;
5
6use behaviortree_derive::Control;
7use tinyscript::SharedRuntime;
8
9use crate::{
10 self as behaviortree_core, BehaviorCreationFn, BehaviorResult, ConstString, behavior_data::BehaviorData,
11 behavior_description::BehaviorDescription, behavior_state::BehaviorState, error::Error, tree::BehaviorTreeElementList,
12};
13
14#[derive(Clone, Copy, Debug)]
16pub enum FallbackKind {
17 Async,
19 Reactive,
21 Standard,
23}
24
25#[derive(Control, Debug)]
74#[behavior(groot2, no_create, no_register, no_register_with)]
75pub struct Fallbacks {
76 kind: FallbackKind,
78 child_idx: usize,
80 skipped: usize,
82 running_child_idx: i32,
84}
85
86#[async_trait::async_trait]
87impl crate::behavior_traits::Behavior for Fallbacks {
88 fn on_start(
89 &mut self,
90 behavior: &mut BehaviorData,
91 _children: &mut BehaviorTreeElementList,
92 _runtime: &SharedRuntime,
93 ) -> Result<(), Error> {
94 self.child_idx = 0;
95 self.skipped = 0;
96 self.running_child_idx = -1;
97 behavior.set_state(BehaviorState::Running);
98 Ok(())
99 }
100
101 async fn tick(
102 &mut self,
103 _behavior: &mut BehaviorData,
104 children: &mut BehaviorTreeElementList,
105 runtime: &SharedRuntime,
106 ) -> BehaviorResult {
107 let children_count = children.len();
108 if matches!(self.kind, FallbackKind::Reactive) {
110 self.child_idx = 0;
111 }
112 while self.child_idx < children_count {
113 let child = &mut children[self.child_idx];
114 let prev_state = child.state();
115 let child_state = child.tick(runtime).await?;
116
117 match child_state {
118 BehaviorState::Failure => {
119 self.child_idx += 1;
120 match self.kind {
121 FallbackKind::Async => {
122 if (prev_state == BehaviorState::Idle) && (self.child_idx < children_count) {
123 return Ok(BehaviorState::Running);
124 }
125 }
126 FallbackKind::Reactive => self.running_child_idx = -1,
127 FallbackKind::Standard => {}
128 }
129 }
130 BehaviorState::Idle => {
131 let behavior: ConstString = match self.kind {
132 FallbackKind::Async => "AsnycFallback".into(),
133 FallbackKind::Reactive => "ReactiveFallback".into(),
134 FallbackKind::Standard => "Fallback".into(),
135 };
136 return Err(Error::State {
137 behavior,
138 state: child_state,
139 });
140 }
141 BehaviorState::Running => {
142 match self.kind {
143 FallbackKind::Async | FallbackKind::Standard => {}
144 #[allow(clippy::cast_possible_truncation)]
145 #[allow(clippy::cast_sign_loss)]
146 #[allow(clippy::cast_possible_wrap)]
147 FallbackKind::Reactive => {
148 if self.running_child_idx != (self.child_idx as i32) && self.running_child_idx != -1 {
150 children[self.running_child_idx as usize].halt_children(runtime)?;
151 }
152 self.running_child_idx = self.child_idx as i32;
153 if self.running_child_idx == -1 {
154 self.running_child_idx = self.child_idx as i32;
155 } else if self.running_child_idx != (self.child_idx as i32) {
156 return Err(Error::Composition {
158 txt: "[ReactiveFallback]: Only a single child can return Running.".into(),
159 });
160 }
161 }
162 }
163 return Ok(child_state);
164 }
165 BehaviorState::Success => {
166 children.reset(runtime)?;
167 children.halt(runtime)?;
168 self.child_idx = 0;
169 self.skipped = 0;
170 self.running_child_idx = -1;
171 return Ok(child_state);
172 }
173 BehaviorState::Skipped => {
174 self.child_idx += 1;
175 self.skipped += 1;
176 }
177 }
178 }
179
180 let all_skipped = self.skipped == children_count;
183 if self.child_idx >= children_count {
184 children.reset(runtime)?;
185 self.child_idx = 0;
186 self.skipped = 0;
187 self.running_child_idx = -1;
188 }
189 if all_skipped {
190 Ok(BehaviorState::Skipped)
191 } else {
192 Ok(BehaviorState::Failure)
193 }
194 }
195}
196
197impl Fallbacks {
198 #[must_use]
200 pub const fn new(kind: FallbackKind) -> Self {
201 Self {
202 kind,
203 child_idx: 0,
204 skipped: 0,
205 running_child_idx: -1,
206 }
207 }
208
209 #[must_use]
211 pub fn create_fn(kind: FallbackKind) -> Box<BehaviorCreationFn> {
212 Box::new(move |_blackboard| Box::new(Self::new(kind)))
213 }
214
215 pub fn register_with(
219 registry: &mut impl behaviortree_core::behavior_traits::BehaviorRegistry,
220 name: &str,
221 kind: FallbackKind,
222 ) -> Result<(), Error> {
223 let bhvr_desc = BehaviorDescription::new(name, name, true);
224 let bhvr_creation_fn = Self::create_fn(kind);
225 registry.add_behavior(bhvr_desc, bhvr_creation_fn)
226 }
227}