1use std::collections::HashMap;
19use std::time::{Duration, Instant};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum NodeStatus {
26 Running,
28 Success,
30 Failure,
32}
33
34impl NodeStatus {
35 pub fn is_running(self) -> bool { self == NodeStatus::Running }
36 pub fn is_success(self) -> bool { self == NodeStatus::Success }
37 pub fn is_failure(self) -> bool { self == NodeStatus::Failure }
38 pub fn is_terminal(self) -> bool { self != NodeStatus::Running }
39
40 pub fn invert(self) -> NodeStatus {
42 match self {
43 NodeStatus::Success => NodeStatus::Failure,
44 NodeStatus::Failure => NodeStatus::Success,
45 NodeStatus::Running => NodeStatus::Running,
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq)]
54pub enum BlackboardValue {
55 Bool(bool),
56 Int(i64),
57 Float(f64),
58 Text(String),
59 Vec2(glam::Vec2),
60 Vec3(glam::Vec3),
61 EntityId(u64),
62 List(Vec<BlackboardValue>),
63 Map(HashMap<String, BlackboardValue>),
64}
65
66impl BlackboardValue {
67 pub fn as_bool(&self) -> Option<bool> {
68 match self {
69 BlackboardValue::Bool(v) => Some(*v),
70 BlackboardValue::Int(v) => Some(*v != 0),
71 BlackboardValue::Float(v) => Some(*v != 0.0),
72 _ => None,
73 }
74 }
75
76 pub fn as_int(&self) -> Option<i64> {
77 match self {
78 BlackboardValue::Int(v) => Some(*v),
79 BlackboardValue::Bool(v) => Some(if *v { 1 } else { 0 }),
80 BlackboardValue::Float(v) => Some(*v as i64),
81 _ => None,
82 }
83 }
84
85 pub fn as_float(&self) -> Option<f64> {
86 match self {
87 BlackboardValue::Float(v) => Some(*v),
88 BlackboardValue::Int(v) => Some(*v as f64),
89 BlackboardValue::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
90 _ => None,
91 }
92 }
93
94 pub fn as_str(&self) -> Option<&str> {
95 match self {
96 BlackboardValue::Text(s) => Some(s.as_str()),
97 _ => None,
98 }
99 }
100
101 pub fn as_vec2(&self) -> Option<glam::Vec2> {
102 match self { BlackboardValue::Vec2(v) => Some(*v), _ => None }
103 }
104
105 pub fn as_vec3(&self) -> Option<glam::Vec3> {
106 match self { BlackboardValue::Vec3(v) => Some(*v), _ => None }
107 }
108
109 pub fn as_entity_id(&self) -> Option<u64> {
110 match self { BlackboardValue::EntityId(id) => Some(*id), _ => None }
111 }
112}
113
114impl From<bool> for BlackboardValue { fn from(v: bool) -> Self { BlackboardValue::Bool(v) } }
115impl From<i64> for BlackboardValue { fn from(v: i64) -> Self { BlackboardValue::Int(v) } }
116impl From<i32> for BlackboardValue { fn from(v: i32) -> Self { BlackboardValue::Int(v as i64) } }
117impl From<f64> for BlackboardValue { fn from(v: f64) -> Self { BlackboardValue::Float(v) } }
118impl From<f32> for BlackboardValue { fn from(v: f32) -> Self { BlackboardValue::Float(v as f64) } }
119impl From<String> for BlackboardValue { fn from(v: String) -> Self { BlackboardValue::Text(v) } }
120impl From<&str> for BlackboardValue { fn from(v: &str) -> Self { BlackboardValue::Text(v.to_string()) } }
121impl From<glam::Vec2> for BlackboardValue { fn from(v: glam::Vec2) -> Self { BlackboardValue::Vec2(v) } }
122impl From<glam::Vec3> for BlackboardValue { fn from(v: glam::Vec3) -> Self { BlackboardValue::Vec3(v) } }
123impl From<u64> for BlackboardValue { fn from(v: u64) -> Self { BlackboardValue::EntityId(v) } }
124
125#[derive(Debug, Default)]
133pub struct Blackboard {
134 entries: HashMap<String, BlackboardEntry>,
135 pub time: f64,
137}
138
139#[derive(Debug, Clone)]
140struct BlackboardEntry {
141 value: BlackboardValue,
142 expires_at: Option<f64>,
144}
145
146impl Blackboard {
147 pub fn new() -> Self { Self::default() }
148
149 pub fn tick(&mut self, dt: f64) {
151 self.time += dt;
152 let t = self.time;
153 self.entries.retain(|_, e| {
154 e.expires_at.map_or(true, |exp| t < exp)
155 });
156 }
157
158 pub fn set<V: Into<BlackboardValue>>(&mut self, key: &str, value: V) {
160 self.entries.insert(key.to_string(), BlackboardEntry {
161 value: value.into(),
162 expires_at: None,
163 });
164 }
165
166 pub fn set_with_ttl<V: Into<BlackboardValue>>(&mut self, key: &str, value: V, ttl: f64) {
168 self.entries.insert(key.to_string(), BlackboardEntry {
169 value: value.into(),
170 expires_at: Some(self.time + ttl),
171 });
172 }
173
174 pub fn get(&self, key: &str) -> Option<&BlackboardValue> {
176 self.entries.get(key).map(|e| &e.value)
177 }
178
179 pub fn get_bool(&self, key: &str) -> Option<bool> {
180 self.get(key)?.as_bool()
181 }
182
183 pub fn get_int(&self, key: &str) -> Option<i64> {
184 self.get(key)?.as_int()
185 }
186
187 pub fn get_float(&self, key: &str) -> Option<f64> {
188 self.get(key)?.as_float()
189 }
190
191 pub fn get_str(&self, key: &str) -> Option<&str> {
192 self.get(key)?.as_str()
193 }
194
195 pub fn get_vec2(&self, key: &str) -> Option<glam::Vec2> {
196 self.get(key)?.as_vec2()
197 }
198
199 pub fn get_vec3(&self, key: &str) -> Option<glam::Vec3> {
200 self.get(key)?.as_vec3()
201 }
202
203 pub fn get_entity(&self, key: &str) -> Option<u64> {
204 self.get(key)?.as_entity_id()
205 }
206
207 pub fn contains(&self, key: &str) -> bool {
208 self.entries.contains_key(key)
209 }
210
211 pub fn remove(&mut self, key: &str) -> Option<BlackboardValue> {
212 self.entries.remove(key).map(|e| e.value)
213 }
214
215 pub fn clear(&mut self) {
216 self.entries.clear();
217 }
218
219 pub fn len(&self) -> usize { self.entries.len() }
221 pub fn is_empty(&self) -> bool { self.entries.is_empty() }
222
223 pub fn iter(&self) -> impl Iterator<Item = (&str, &BlackboardValue)> {
225 self.entries.iter().map(|(k, e)| (k.as_str(), &e.value))
226 }
227
228 pub fn float_gt(&self, key: &str, threshold: f64) -> bool {
230 self.get_float(key).map_or(false, |v| v > threshold)
231 }
232
233 pub fn float_lt(&self, key: &str, threshold: f64) -> bool {
234 self.get_float(key).map_or(false, |v| v < threshold)
235 }
236
237 pub fn float_gte(&self, key: &str, threshold: f64) -> bool {
238 self.get_float(key).map_or(false, |v| v >= threshold)
239 }
240
241 pub fn float_lte(&self, key: &str, threshold: f64) -> bool {
242 self.get_float(key).map_or(false, |v| v <= threshold)
243 }
244
245 pub fn increment(&mut self, key: &str, delta: i64) {
247 let current = self.get_int(key).unwrap_or(0);
248 self.set(key, current + delta);
249 }
250
251 pub fn decrement(&mut self, key: &str, delta: i64) {
253 self.increment(key, -delta);
254 }
255
256 pub fn toggle(&mut self, key: &str) {
258 let current = self.get_bool(key).unwrap_or(false);
259 self.set(key, !current);
260 }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum ParallelPolicy {
268 RequireAll,
270 RequireOne,
272 RequireN(usize),
274}
275
276impl Default for ParallelPolicy {
277 fn default() -> Self { ParallelPolicy::RequireAll }
278}
279
280#[derive(Debug, Clone)]
284pub enum DecoratorKind {
285 Invert,
287 AlwaysSucceed,
289 AlwaysFail,
291 UntilSuccess,
293 UntilFailure,
295 Repeat { count: u32 },
297 RepeatForever,
299 Timeout { timeout_secs: f32 },
301 Cooldown { cooldown_secs: f32 },
303 BlackboardGuard { key: String, expected: bool },
305 BlackboardCheck { key: String, expected: BlackboardValue },
307}
308
309pub enum BehaviorNode {
317 Sequence {
320 name: String,
321 children: Vec<BehaviorNode>,
322 cursor: usize,
324 },
325
326 Selector {
329 name: String,
330 children: Vec<BehaviorNode>,
331 cursor: usize,
332 },
333
334 Parallel {
336 name: String,
337 children: Vec<BehaviorNode>,
338 policy: ParallelPolicy,
339 },
340
341 Decorator {
343 name: String,
344 kind: DecoratorKind,
345 child: Box<BehaviorNode>,
346 state: DecoratorState,
348 },
349
350 Leaf {
352 name: String,
353 on_enter: Option<Box<dyn FnMut(&mut Blackboard)>>,
355 on_tick: Box<dyn FnMut(&mut Blackboard, f32) -> NodeStatus>,
357 on_exit: Option<Box<dyn FnMut(&mut Blackboard, NodeStatus)>>,
359 entered: bool,
361 },
362
363 SubtreeRef {
366 name: String,
367 },
368}
369
370impl std::fmt::Debug for BehaviorNode {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 match self {
373 BehaviorNode::Sequence { name, .. } => write!(f, "Sequence({name})"),
374 BehaviorNode::Selector { name, .. } => write!(f, "Selector({name})"),
375 BehaviorNode::Parallel { name, .. } => write!(f, "Parallel({name})"),
376 BehaviorNode::Decorator { name, kind, .. }=> write!(f, "Decorator({name}, {kind:?})"),
377 BehaviorNode::Leaf { name, .. } => write!(f, "Leaf({name})"),
378 BehaviorNode::SubtreeRef{ name } => write!(f, "SubtreeRef({name})"),
379 }
380 }
381}
382
383#[derive(Debug, Default, Clone)]
387pub struct DecoratorState {
388 pub repeat_count: u32,
390 pub activated_at: Option<Instant>,
392 pub last_tick_time: Option<Instant>,
394 pub last_tick_sim: f64,
396}
397
398impl DecoratorState {
399 fn reset(&mut self) {
400 self.repeat_count = 0;
401 self.activated_at = None;
402 self.last_tick_time = None;
403 self.last_tick_sim = 0.0;
404 }
405}
406
407impl BehaviorNode {
410 pub fn tick(
416 &mut self,
417 dt: f32,
418 bb: &mut Blackboard,
419 registry: &SubtreeRegistry,
420 ) -> NodeStatus {
421 match self {
422 BehaviorNode::Sequence { children, cursor, .. } => {
424 while *cursor < children.len() {
425 let status = children[*cursor].tick(dt, bb, registry);
426 match status {
427 NodeStatus::Success => { *cursor += 1; }
428 NodeStatus::Running => return NodeStatus::Running,
429 NodeStatus::Failure => {
430 *cursor = 0;
431 return NodeStatus::Failure;
432 }
433 }
434 }
435 *cursor = 0;
436 NodeStatus::Success
437 }
438
439 BehaviorNode::Selector { children, cursor, .. } => {
441 while *cursor < children.len() {
442 let status = children[*cursor].tick(dt, bb, registry);
443 match status {
444 NodeStatus::Failure => { *cursor += 1; }
445 NodeStatus::Success => {
446 *cursor = 0;
447 return NodeStatus::Success;
448 }
449 NodeStatus::Running => return NodeStatus::Running,
450 }
451 }
452 *cursor = 0;
453 NodeStatus::Failure
454 }
455
456 BehaviorNode::Parallel { children, policy, .. } => {
458 let mut successes = 0usize;
459 let mut failures = 0usize;
460 let total = children.len();
461
462 for child in children.iter_mut() {
463 match child.tick(dt, bb, registry) {
464 NodeStatus::Success => successes += 1,
465 NodeStatus::Failure => failures += 1,
466 NodeStatus::Running => {}
467 }
468 }
469
470 match *policy {
471 ParallelPolicy::RequireAll => {
472 if failures > 0 { NodeStatus::Failure }
473 else if successes == total { NodeStatus::Success }
474 else { NodeStatus::Running }
475 }
476 ParallelPolicy::RequireOne => {
477 if successes > 0 { NodeStatus::Success }
478 else if failures == total { NodeStatus::Failure }
479 else { NodeStatus::Running }
480 }
481 ParallelPolicy::RequireN(n) => {
482 if successes >= n { NodeStatus::Success }
483 else if total - failures < n { NodeStatus::Failure }
484 else { NodeStatus::Running }
485 }
486 }
487 }
488
489 BehaviorNode::Decorator { kind, child, state, .. } => {
491 tick_decorator(kind, child, state, dt, bb, registry)
492 }
493
494 BehaviorNode::Leaf { on_enter, on_tick, on_exit, entered, .. } => {
496 if !*entered {
497 *entered = true;
498 if let Some(enter_fn) = on_enter {
499 enter_fn(bb);
500 }
501 }
502 let status = on_tick(bb, dt);
503 if status.is_terminal() {
504 *entered = false;
505 if let Some(exit_fn) = on_exit {
506 exit_fn(bb, status);
507 }
508 }
509 status
510 }
511
512 BehaviorNode::SubtreeRef { name } => {
514 log::warn!("SubtreeRef({name}) ticked directly — subtree not found");
518 NodeStatus::Failure
519 }
520 }
521 }
522
523 pub fn reset(&mut self) {
526 match self {
527 BehaviorNode::Sequence { children, cursor, .. } => {
528 *cursor = 0;
529 for c in children.iter_mut() { c.reset(); }
530 }
531 BehaviorNode::Selector { children, cursor, .. } => {
532 *cursor = 0;
533 for c in children.iter_mut() { c.reset(); }
534 }
535 BehaviorNode::Parallel { children, .. } => {
536 for c in children.iter_mut() { c.reset(); }
537 }
538 BehaviorNode::Decorator { child, state, .. } => {
539 state.reset();
540 child.reset();
541 }
542 BehaviorNode::Leaf { entered, .. } => {
543 *entered = false;
544 }
545 BehaviorNode::SubtreeRef { .. } => {}
546 }
547 }
548
549 pub fn name(&self) -> &str {
551 match self {
552 BehaviorNode::Sequence { name, .. } => name,
553 BehaviorNode::Selector { name, .. } => name,
554 BehaviorNode::Parallel { name, .. } => name,
555 BehaviorNode::Decorator { name, .. } => name,
556 BehaviorNode::Leaf { name, .. } => name,
557 BehaviorNode::SubtreeRef{ name } => name,
558 }
559 }
560
561 pub fn collect_names(&self, out: &mut Vec<String>) {
563 out.push(self.name().to_string());
564 match self {
565 BehaviorNode::Sequence { children, .. }
566 | BehaviorNode::Selector{ children, .. }
567 | BehaviorNode::Parallel{ children, .. } => {
568 for c in children { c.collect_names(out); }
569 }
570 BehaviorNode::Decorator { child, .. } => child.collect_names(out),
571 _ => {}
572 }
573 }
574}
575
576fn tick_decorator(
579 kind: &mut DecoratorKind,
580 child: &mut BehaviorNode,
581 state: &mut DecoratorState,
582 dt: f32,
583 bb: &mut Blackboard,
584 registry: &SubtreeRegistry,
585) -> NodeStatus {
586 match kind {
587 DecoratorKind::Invert => child.tick(dt, bb, registry).invert(),
588
589 DecoratorKind::AlwaysSucceed => {
590 child.tick(dt, bb, registry);
591 NodeStatus::Success
592 }
593
594 DecoratorKind::AlwaysFail => {
595 child.tick(dt, bb, registry);
596 NodeStatus::Failure
597 }
598
599 DecoratorKind::UntilSuccess => {
600 match child.tick(dt, bb, registry) {
601 NodeStatus::Success => NodeStatus::Success,
602 _ => NodeStatus::Running,
603 }
604 }
605
606 DecoratorKind::UntilFailure => {
607 match child.tick(dt, bb, registry) {
608 NodeStatus::Failure => NodeStatus::Failure,
609 _ => NodeStatus::Running,
610 }
611 }
612
613 DecoratorKind::Repeat { count } => {
614 let target = *count;
615 loop {
616 match child.tick(dt, bb, registry) {
617 NodeStatus::Running => return NodeStatus::Running,
618 NodeStatus::Failure => {
619 state.repeat_count = 0;
620 child.reset();
621 return NodeStatus::Failure;
622 }
623 NodeStatus::Success => {
624 state.repeat_count += 1;
625 child.reset();
626 if state.repeat_count >= target {
627 state.repeat_count = 0;
628 return NodeStatus::Success;
629 }
630 }
631 }
632 }
633 }
634
635 DecoratorKind::RepeatForever => {
636 match child.tick(dt, bb, registry) {
637 NodeStatus::Success | NodeStatus::Failure => {
638 child.reset();
639 }
640 NodeStatus::Running => {}
641 }
642 NodeStatus::Running
643 }
644
645 DecoratorKind::Timeout { timeout_secs } => {
646 let limit = *timeout_secs;
647 let now = Instant::now();
648 let started = state.activated_at.get_or_insert(now);
649 if now.duration_since(*started) >= Duration::from_secs_f32(limit) {
650 state.activated_at = None;
651 child.reset();
652 return NodeStatus::Failure;
653 }
654 let status = child.tick(dt, bb, registry);
655 if status.is_terminal() {
656 state.activated_at = None;
657 }
658 status
659 }
660
661 DecoratorKind::Cooldown { cooldown_secs } => {
662 let cooldown = Duration::from_secs_f32(*cooldown_secs);
663 let now = Instant::now();
664 if let Some(last) = state.last_tick_time {
665 if now.duration_since(last) < cooldown {
666 return NodeStatus::Failure;
667 }
668 }
669 state.last_tick_time = Some(now);
670 child.tick(dt, bb, registry)
671 }
672
673 DecoratorKind::BlackboardGuard { key, expected } => {
674 let key_clone = key.clone();
675 let exp = *expected;
676 let ok = bb.get_bool(&key_clone).unwrap_or(false) == exp;
677 if ok { child.tick(dt, bb, registry) } else { NodeStatus::Failure }
678 }
679
680 DecoratorKind::BlackboardCheck { key, expected } => {
681 let key_clone = key.clone();
682 match bb.get(&key_clone) {
683 Some(v) if v == expected => NodeStatus::Success,
684 _ => NodeStatus::Failure,
685 }
686 }
687 }
688}
689
690pub struct SubtreeRegistry {
697 builders: HashMap<String, Box<dyn Fn() -> BehaviorNode>>,
698}
699
700impl Default for SubtreeRegistry {
701 fn default() -> Self { Self { builders: HashMap::new() } }
702}
703
704impl SubtreeRegistry {
705 pub fn new() -> Self { Self::default() }
706
707 pub fn register<F>(&mut self, name: &str, factory: F)
709 where
710 F: Fn() -> BehaviorNode + 'static,
711 {
712 self.builders.insert(name.to_string(), Box::new(factory));
713 }
714
715 pub fn instantiate(&self, name: &str) -> Option<BehaviorNode> {
717 self.builders.get(name).map(|f| f())
718 }
719
720 pub fn contains(&self, name: &str) -> bool {
721 self.builders.contains_key(name)
722 }
723
724 pub fn names(&self) -> impl Iterator<Item = &str> {
725 self.builders.keys().map(|s| s.as_str())
726 }
727}
728
729impl std::fmt::Debug for SubtreeRegistry {
730 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
731 let names: Vec<&str> = self.names().collect();
732 f.debug_struct("SubtreeRegistry").field("subtrees", &names).finish()
733 }
734}
735
736#[derive(Debug)]
741pub struct BehaviorTree {
742 pub name: String,
744 root: BehaviorNode,
745 pub bb: Blackboard,
746 pub registry: SubtreeRegistry,
747 pub last_status: NodeStatus,
749 pub sim_time: f64,
751 pub active: bool,
753}
754
755impl BehaviorTree {
756 pub fn new(name: &str, root: BehaviorNode) -> Self {
757 Self {
758 name: name.to_string(),
759 root,
760 bb: Blackboard::new(),
761 registry: SubtreeRegistry::new(),
762 last_status: NodeStatus::Running,
763 sim_time: 0.0,
764 active: true,
765 }
766 }
767
768 pub fn tick(&mut self, dt: f32) -> NodeStatus {
771 if !self.active {
772 return self.last_status;
773 }
774 self.sim_time += dt as f64;
775 self.bb.tick(dt as f64);
776 let status = tick_with_registry(&mut self.root, dt, &mut self.bb, &self.registry);
777 self.last_status = status;
778 status
779 }
780
781 pub fn reset(&mut self) {
783 self.root.reset();
784 self.last_status = NodeStatus::Running;
785 }
786
787 pub fn set_active(&mut self, active: bool) { self.active = active; }
789
790 pub fn blackboard(&self) -> &Blackboard { &self.bb }
792
793 pub fn blackboard_mut(&mut self) -> &mut Blackboard { &mut self.bb }
795
796 pub fn registry_mut(&mut self) -> &mut SubtreeRegistry { &mut self.registry }
798
799 pub fn node_names(&self) -> Vec<String> {
801 let mut names = Vec::new();
802 self.root.collect_names(&mut names);
803 names
804 }
805}
806
807fn tick_with_registry(
809 node: &mut BehaviorNode,
810 dt: f32,
811 bb: &mut Blackboard,
812 registry: &SubtreeRegistry,
813) -> NodeStatus {
814 if let BehaviorNode::SubtreeRef { name } = node {
818 match registry.instantiate(name) {
819 Some(mut subtree) => subtree.tick(dt, bb, registry),
820 None => {
821 log::warn!("SubtreeRef: unknown subtree '{name}'");
822 NodeStatus::Failure
823 }
824 }
825 } else {
826 node.tick(dt, bb, registry)
827 }
828}
829
830pub struct TreeBuilder {
845 stack: Vec<BuildFrame>,
847 result: Option<BehaviorNode>,
849}
850
851struct BuildFrame {
852 kind: FrameKind,
853 name: String,
854 children: Vec<BehaviorNode>,
855 dec_kind: Option<DecoratorKind>,
857 par_policy: Option<ParallelPolicy>,
859}
860
861#[derive(Debug, Clone, Copy, PartialEq, Eq)]
862enum FrameKind { Sequence, Selector, Parallel, Decorator }
863
864impl TreeBuilder {
865 fn new_frame(kind: FrameKind, name: &str) -> BuildFrame {
866 BuildFrame { kind, name: name.to_string(), children: Vec::new(),
867 dec_kind: None, par_policy: None }
868 }
869
870 pub fn sequence(name: &str) -> Self {
872 let mut b = TreeBuilder { stack: Vec::new(), result: None };
873 b.stack.push(Self::new_frame(FrameKind::Sequence, name));
874 b
875 }
876
877 pub fn selector(name: &str) -> Self {
879 let mut b = TreeBuilder { stack: Vec::new(), result: None };
880 b.stack.push(Self::new_frame(FrameKind::Selector, name));
881 b
882 }
883
884 pub fn parallel(name: &str, policy: ParallelPolicy) -> Self {
886 let mut b = TreeBuilder { stack: Vec::new(), result: None };
887 let mut frame = Self::new_frame(FrameKind::Parallel, name);
888 frame.par_policy = Some(policy);
889 b.stack.push(frame);
890 b
891 }
892
893 pub fn sequence_child(mut self, name: &str) -> Self {
895 self.stack.push(Self::new_frame(FrameKind::Sequence, name));
896 self
897 }
898
899 pub fn selector_child(mut self, name: &str) -> Self {
901 self.stack.push(Self::new_frame(FrameKind::Selector, name));
902 self
903 }
904
905 pub fn parallel_child(mut self, name: &str, policy: ParallelPolicy) -> Self {
907 let mut frame = Self::new_frame(FrameKind::Parallel, name);
908 frame.par_policy = Some(policy);
909 self.stack.push(frame);
910 self
911 }
912
913 pub fn decorator(mut self, name: &str, kind: DecoratorKind) -> Self {
915 let mut frame = Self::new_frame(FrameKind::Decorator, name);
916 frame.dec_kind = Some(kind);
917 self.stack.push(frame);
918 self
919 }
920
921 pub fn leaf<F>(mut self, name: &str, tick_fn: F) -> Self
923 where
924 F: FnMut(&mut Blackboard, f32) -> NodeStatus + 'static,
925 {
926 let node = BehaviorNode::Leaf {
927 name: name.to_string(),
928 on_enter: None,
929 on_tick: Box::new(tick_fn),
930 on_exit: None,
931 entered: false,
932 };
933 self.push_child(node);
934 self
935 }
936
937 pub fn leaf_full<Enter, Tick, Exit>(
939 mut self,
940 name: &str,
941 enter_fn: Enter,
942 tick_fn: Tick,
943 exit_fn: Exit,
944 ) -> Self
945 where
946 Enter: FnMut(&mut Blackboard) + 'static,
947 Tick: FnMut(&mut Blackboard, f32) -> NodeStatus + 'static,
948 Exit: FnMut(&mut Blackboard, NodeStatus) + 'static,
949 {
950 let node = BehaviorNode::Leaf {
951 name: name.to_string(),
952 on_enter: Some(Box::new(enter_fn)),
953 on_tick: Box::new(tick_fn),
954 on_exit: Some(Box::new(exit_fn)),
955 entered: false,
956 };
957 self.push_child(node);
958 self
959 }
960
961 pub fn subtree_ref(mut self, name: &str) -> Self {
963 self.push_child(BehaviorNode::SubtreeRef { name: name.to_string() });
964 self
965 }
966
967 pub fn node(mut self, node: BehaviorNode) -> Self {
969 self.push_child(node);
970 self
971 }
972
973 pub fn end(mut self) -> Self {
975 let completed = self.pop_frame();
976 if self.stack.is_empty() {
977 self.result = Some(completed);
978 } else {
979 self.push_child(completed);
980 }
981 self
982 }
983
984 pub fn build(mut self) -> BehaviorNode {
987 while !self.stack.is_empty() {
989 let completed = self.pop_frame();
990 if self.stack.is_empty() {
991 self.result = Some(completed);
992 } else {
993 self.push_child(completed);
994 }
995 }
996 self.result.expect("TreeBuilder::build called with no nodes")
997 }
998
999 pub fn into_tree(self, tree_name: &str) -> BehaviorTree {
1001 let root = self.build();
1002 BehaviorTree::new(tree_name, root)
1003 }
1004
1005 fn push_child(&mut self, node: BehaviorNode) {
1008 if let Some(frame) = self.stack.last_mut() {
1009 frame.children.push(node);
1010 } else {
1011 self.result = Some(node);
1013 }
1014 }
1015
1016 fn pop_frame(&mut self) -> BehaviorNode {
1017 let frame = self.stack.pop().expect("TreeBuilder: pop_frame on empty stack");
1018 match frame.kind {
1019 FrameKind::Sequence => BehaviorNode::Sequence {
1020 name: frame.name,
1021 children: frame.children,
1022 cursor: 0,
1023 },
1024 FrameKind::Selector => BehaviorNode::Selector {
1025 name: frame.name,
1026 children: frame.children,
1027 cursor: 0,
1028 },
1029 FrameKind::Parallel => BehaviorNode::Parallel {
1030 name: frame.name,
1031 children: frame.children,
1032 policy: frame.par_policy.unwrap_or_default(),
1033 },
1034 FrameKind::Decorator => {
1035 let kind = frame.dec_kind.expect("Decorator frame missing kind");
1036 let child = frame.children.into_iter().next()
1037 .expect("Decorator frame must have exactly one child");
1038 BehaviorNode::Decorator {
1039 name: frame.name,
1040 kind,
1041 child: Box::new(child),
1042 state: DecoratorState::default(),
1043 }
1044 }
1045 }
1046 }
1047}
1048
1049pub fn leaf<F>(name: &str, tick_fn: F) -> BehaviorNode
1053where
1054 F: FnMut(&mut Blackboard, f32) -> NodeStatus + 'static,
1055{
1056 BehaviorNode::Leaf {
1057 name: name.to_string(),
1058 on_enter: None,
1059 on_tick: Box::new(tick_fn),
1060 on_exit: None,
1061 entered: false,
1062 }
1063}
1064
1065pub fn sequence(name: &str, children: Vec<BehaviorNode>) -> BehaviorNode {
1067 BehaviorNode::Sequence { name: name.to_string(), children, cursor: 0 }
1068}
1069
1070pub fn selector(name: &str, children: Vec<BehaviorNode>) -> BehaviorNode {
1072 BehaviorNode::Selector { name: name.to_string(), children, cursor: 0 }
1073}
1074
1075pub fn parallel(name: &str, children: Vec<BehaviorNode>, policy: ParallelPolicy) -> BehaviorNode {
1077 BehaviorNode::Parallel { name: name.to_string(), children, policy }
1078}
1079
1080pub fn invert(name: &str, child: BehaviorNode) -> BehaviorNode {
1082 BehaviorNode::Decorator {
1083 name: name.to_string(),
1084 kind: DecoratorKind::Invert,
1085 child: Box::new(child),
1086 state: DecoratorState::default(),
1087 }
1088}
1089
1090pub fn repeat(name: &str, count: u32, child: BehaviorNode) -> BehaviorNode {
1092 BehaviorNode::Decorator {
1093 name: name.to_string(),
1094 kind: DecoratorKind::Repeat { count },
1095 child: Box::new(child),
1096 state: DecoratorState::default(),
1097 }
1098}
1099
1100pub fn timeout(name: &str, secs: f32, child: BehaviorNode) -> BehaviorNode {
1102 BehaviorNode::Decorator {
1103 name: name.to_string(),
1104 kind: DecoratorKind::Timeout { timeout_secs: secs },
1105 child: Box::new(child),
1106 state: DecoratorState::default(),
1107 }
1108}
1109
1110pub fn cooldown(name: &str, secs: f32, child: BehaviorNode) -> BehaviorNode {
1112 BehaviorNode::Decorator {
1113 name: name.to_string(),
1114 kind: DecoratorKind::Cooldown { cooldown_secs: secs },
1115 child: Box::new(child),
1116 state: DecoratorState::default(),
1117 }
1118}
1119
1120#[cfg(test)]
1123mod tests {
1124 use super::*;
1125
1126 fn succeed() -> BehaviorNode {
1127 leaf("succeed", |_, _| NodeStatus::Success)
1128 }
1129 fn fail() -> BehaviorNode {
1130 leaf("fail", |_, _| NodeStatus::Failure)
1131 }
1132
1133 #[test]
1134 fn sequence_all_succeed() {
1135 let mut bb = Blackboard::new();
1136 let reg = SubtreeRegistry::new();
1137 let mut node = sequence("s", vec![succeed(), succeed(), succeed()]);
1138 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Success);
1139 }
1140
1141 #[test]
1142 fn sequence_first_fails() {
1143 let mut bb = Blackboard::new();
1144 let reg = SubtreeRegistry::new();
1145 let mut node = sequence("s", vec![fail(), succeed()]);
1146 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Failure);
1147 }
1148
1149 #[test]
1150 fn selector_first_succeeds() {
1151 let mut bb = Blackboard::new();
1152 let reg = SubtreeRegistry::new();
1153 let mut node = selector("sel", vec![succeed(), fail()]);
1154 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Success);
1155 }
1156
1157 #[test]
1158 fn selector_all_fail() {
1159 let mut bb = Blackboard::new();
1160 let reg = SubtreeRegistry::new();
1161 let mut node = selector("sel", vec![fail(), fail()]);
1162 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Failure);
1163 }
1164
1165 #[test]
1166 fn invert_decorator() {
1167 let mut bb = Blackboard::new();
1168 let reg = SubtreeRegistry::new();
1169 let mut node = invert("inv", succeed());
1170 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Failure);
1171 }
1172
1173 #[test]
1174 fn blackboard_set_get() {
1175 let mut bb = Blackboard::new();
1176 bb.set("health", 80.0f64);
1177 assert!(bb.float_gt("health", 50.0));
1178 assert!(!bb.float_lt("health", 50.0));
1179 }
1180
1181 #[test]
1182 fn blackboard_ttl_expiry() {
1183 let mut bb = Blackboard::new();
1184 bb.set_with_ttl("temp", true, 1.0);
1185 assert!(bb.contains("temp"));
1186 bb.tick(2.0);
1187 assert!(!bb.contains("temp"));
1188 }
1189
1190 #[test]
1191 fn repeat_decorator() {
1192 let mut bb = Blackboard::new();
1193 let reg = SubtreeRegistry::new();
1194 let mut node = repeat("rep", 3, succeed());
1195 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Success);
1196 }
1197
1198 #[test]
1199 fn parallel_require_all() {
1200 let mut bb = Blackboard::new();
1201 let reg = SubtreeRegistry::new();
1202 let mut node = parallel("par", vec![succeed(), succeed()], ParallelPolicy::RequireAll);
1203 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Success);
1204 }
1205
1206 #[test]
1207 fn parallel_require_one_first_succeeds() {
1208 let mut bb = Blackboard::new();
1209 let reg = SubtreeRegistry::new();
1210 let mut node = parallel("par", vec![succeed(), fail()], ParallelPolicy::RequireOne);
1211 assert_eq!(node.tick(0.016, &mut bb, ®), NodeStatus::Success);
1212 }
1213
1214 #[test]
1215 fn tree_builder_round_trip() {
1216 let root = TreeBuilder::sequence("root")
1217 .leaf("a", |_, _| NodeStatus::Success)
1218 .leaf("b", |_, _| NodeStatus::Success)
1219 .build();
1220 let mut tree = BehaviorTree::new("test", root);
1221 assert_eq!(tree.tick(0.016), NodeStatus::Success);
1222 }
1223}