behavior_tree_lite/
context.rs1use crate::{
2 BBMap, BehaviorCallback, BehaviorNodeContainer, BehaviorResult, Blackboard, BlackboardValue,
3 PortType, Symbol,
4};
5use std::{any::Any, rc::Rc, str::FromStr};
6
7#[derive(Default)]
10pub struct DebugIgnore<T: ?Sized>(pub T);
11
12impl<T: ?Sized> std::fmt::Debug for DebugIgnore<T> {
13 #[inline]
14 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
15 write!(f, "...")
16 }
17}
18
19impl<T: ?Sized> std::ops::Deref for DebugIgnore<T> {
20 type Target = T;
21
22 #[inline]
23 fn deref(&self) -> &Self::Target {
24 &self.0
25 }
26}
27
28#[derive(Default, Debug)]
29pub struct Context {
30 pub(crate) blackboard: Blackboard,
31 pub(crate) blackboard_map: BBMap,
32 pub(crate) child_nodes: DebugIgnore<Vec<BehaviorNodeContainer>>,
33 strict: bool,
34}
35
36impl Context {
37 pub fn new(blackboard: Blackboard) -> Self {
38 Self {
39 blackboard,
40 blackboard_map: BBMap::new(),
41 child_nodes: DebugIgnore(vec![]),
42 strict: true,
43 }
44 }
45
46 pub fn take_blackboard(self) -> Blackboard {
47 self.blackboard
48 }
49
50 pub fn strict(&self) -> bool {
51 self.strict
52 }
53
54 pub fn set_strict(&mut self, b: bool) {
55 self.strict = b;
56 }
57
58 pub fn tick_child(&mut self, idx: usize, arg: BehaviorCallback) -> Option<BehaviorResult> {
59 let mut children = std::mem::take(&mut self.child_nodes.0);
61 let res = children.get_mut(idx).map(|child| {
62 let res = child.tick(arg, self);
63 child.last_result = Some(res);
64 res
65 });
66 self.child_nodes.0 = children;
67 res
68 }
69
70 pub fn num_children(&self) -> usize {
71 self.child_nodes.len()
72 }
73}
74
75impl Context {
76 pub fn get<T: 'static>(&self, key: impl Into<Symbol>) -> Option<&T> {
79 let key: Symbol = key.into();
80 let mapped = self.blackboard_map.get(&key);
81 let mapped = match mapped {
82 None => &key,
83 Some(BlackboardValue::Ref(mapped, ty)) => {
84 if matches!(*ty, PortType::Input | PortType::InOut) {
85 mapped
86 } else {
87 if self.strict {
88 panic!("Port {:?} is not specified as input or inout port", key);
89 }
90 return None;
91 }
92 }
93 Some(BlackboardValue::Literal(mapped)) => {
94 return (mapped as &dyn Any).downcast_ref();
95 }
96 };
97
98 self.blackboard.get(mapped).and_then(|val| {
99 val.downcast_ref()
101 })
102 }
103
104 pub fn get_any(&self, key: impl Into<Symbol>) -> Option<Rc<dyn Any>> {
106 let key: Symbol = key.into();
107 let mapped = self.blackboard_map.get(&key);
108 let mapped = match mapped {
109 None => &key,
110 Some(BlackboardValue::Ref(mapped, ty)) => {
111 if matches!(*ty, PortType::Input | PortType::InOut) {
112 mapped
113 } else {
114 if self.strict {
115 panic!("Port {:?} is not specified as input or inout port", key);
116 }
117 return None;
118 }
119 }
120 Some(BlackboardValue::Literal(mapped)) => {
121 return Some(Rc::new(mapped.clone()));
122 }
123 };
124
125 self.blackboard.get(mapped).cloned()
126 }
127
128 pub fn get_parse<F>(&self, key: impl Into<Symbol> + Copy) -> Option<F>
130 where
131 F: FromStr + Copy + 'static,
132 {
133 self.get::<F>(key).copied().or_else(|| {
134 self.get::<String>(key)
135 .and_then(|val| val.parse::<F>().ok())
136 })
137 }
138
139 pub fn set<T: 'static>(&mut self, key: impl Into<Symbol>, val: T) {
140 if let Some(key) = self.map_out_key(key) {
141 self.blackboard.insert(key, Rc::new(val));
142 }
143 }
144
145 pub fn set_any(&mut self, key: impl Into<Symbol>, val: Rc<dyn Any>) {
146 if let Some(key) = self.map_out_key(key) {
147 self.blackboard.insert(key, val);
148 }
149 }
150
151 fn map_out_key(&self, key: impl Into<Symbol>) -> Option<Symbol> {
152 let key = key.into();
153 let mapped = self.blackboard_map.get(&key);
154 match mapped {
155 None => Some(key),
156 Some(BlackboardValue::Ref(mapped, ty)) => {
157 if matches!(ty, PortType::Output | PortType::InOut) {
158 Some(*mapped)
159 } else {
160 if self.strict {
161 panic!("Port {:?} is not specified as output or inout port", key);
162 }
163 None
164 }
165 }
166 Some(BlackboardValue::Literal(_)) => panic!("Cannot write to a literal!"),
167 }
168 }
169
170 }