diff --git a/addons/beehave/blackboard.gd b/addons/beehave/blackboard.gd
new file mode 100644
index 0000000..4cff1ce
--- /dev/null
+++ b/addons/beehave/blackboard.gd
@@ -0,0 +1,24 @@
+extends Reference
+
+var blackboard = {}
+
+func set(key, value, blackboard_name = 'default'):
+ if not blackboard.has(blackboard_name):
+ blackboard[blackboard_name] = {}
+
+ blackboard[blackboard_name][key] = value
+
+
+func get(key, default_value = null, blackboard_name = 'default'):
+ if has(key, blackboard_name):
+ return blackboard[blackboard_name].get(key, default_value)
+ return default_value
+
+
+func has(key, blackboard_name = 'default'):
+ return blackboard.has(blackboard_name) and blackboard[blackboard_name].has(key) and blackboard[blackboard_name][key] != null
+
+
+func erase(key, blackboard_name = 'default'):
+ if blackboard.has(blackboard_name):
+ blackboard[blackboard_name][key] = null
diff --git a/addons/beehave/icons/action.svg b/addons/beehave/icons/action.svg
new file mode 100644
index 0000000..7c224d5
--- /dev/null
+++ b/addons/beehave/icons/action.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/action.svg.import b/addons/beehave/icons/action.svg.import
new file mode 100644
index 0000000..ee085b3
--- /dev/null
+++ b/addons/beehave/icons/action.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/action.svg-e8a91246d0ba9ba3cf84290d65648f06.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/action.svg"
+dest_files=[ "res://.import/action.svg-e8a91246d0ba9ba3cf84290d65648f06.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/blackboard.svg b/addons/beehave/icons/blackboard.svg
new file mode 100644
index 0000000..af009c6
--- /dev/null
+++ b/addons/beehave/icons/blackboard.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/blackboard.svg.import b/addons/beehave/icons/blackboard.svg.import
new file mode 100644
index 0000000..fd5d540
--- /dev/null
+++ b/addons/beehave/icons/blackboard.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/blackboard.svg"
+dest_files=[ "res://.import/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/category_bt.svg b/addons/beehave/icons/category_bt.svg
new file mode 100644
index 0000000..ab92d03
--- /dev/null
+++ b/addons/beehave/icons/category_bt.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/category_bt.svg.import b/addons/beehave/icons/category_bt.svg.import
new file mode 100644
index 0000000..f8400a5
--- /dev/null
+++ b/addons/beehave/icons/category_bt.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/category_bt.svg"
+dest_files=[ "res://.import/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/category_composite.svg b/addons/beehave/icons/category_composite.svg
new file mode 100644
index 0000000..0f157de
--- /dev/null
+++ b/addons/beehave/icons/category_composite.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/category_composite.svg.import b/addons/beehave/icons/category_composite.svg.import
new file mode 100644
index 0000000..75f2a39
--- /dev/null
+++ b/addons/beehave/icons/category_composite.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/category_composite.svg"
+dest_files=[ "res://.import/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/category_decorator.svg b/addons/beehave/icons/category_decorator.svg
new file mode 100644
index 0000000..02c0375
--- /dev/null
+++ b/addons/beehave/icons/category_decorator.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/category_decorator.svg.import b/addons/beehave/icons/category_decorator.svg.import
new file mode 100644
index 0000000..b4687fd
--- /dev/null
+++ b/addons/beehave/icons/category_decorator.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/category_decorator.svg"
+dest_files=[ "res://.import/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/condition.svg b/addons/beehave/icons/condition.svg
new file mode 100644
index 0000000..5d7bbb0
--- /dev/null
+++ b/addons/beehave/icons/condition.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/condition.svg.import b/addons/beehave/icons/condition.svg.import
new file mode 100644
index 0000000..9e499d7
--- /dev/null
+++ b/addons/beehave/icons/condition.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/condition.svg-57892684b10a64086f68c09c388b17e5.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/condition.svg"
+dest_files=[ "res://.import/condition.svg-57892684b10a64086f68c09c388b17e5.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/fail.svg b/addons/beehave/icons/fail.svg
new file mode 100644
index 0000000..d96bb98
--- /dev/null
+++ b/addons/beehave/icons/fail.svg
@@ -0,0 +1,66 @@
+
+
diff --git a/addons/beehave/icons/fail.svg.import b/addons/beehave/icons/fail.svg.import
new file mode 100644
index 0000000..103fcc3
--- /dev/null
+++ b/addons/beehave/icons/fail.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/fail.svg-85247bd8065819a11b971d222d3d43bb.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/fail.svg"
+dest_files=[ "res://.import/fail.svg-85247bd8065819a11b971d222d3d43bb.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/inverter.svg b/addons/beehave/icons/inverter.svg
new file mode 100644
index 0000000..45b8e97
--- /dev/null
+++ b/addons/beehave/icons/inverter.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/inverter.svg.import b/addons/beehave/icons/inverter.svg.import
new file mode 100644
index 0000000..a3b53d7
--- /dev/null
+++ b/addons/beehave/icons/inverter.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/inverter.svg"
+dest_files=[ "res://.import/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/limiter.svg b/addons/beehave/icons/limiter.svg
new file mode 100644
index 0000000..2d6bbb6
--- /dev/null
+++ b/addons/beehave/icons/limiter.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/limiter.svg.import b/addons/beehave/icons/limiter.svg.import
new file mode 100644
index 0000000..8a02cf0
--- /dev/null
+++ b/addons/beehave/icons/limiter.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/limiter.svg"
+dest_files=[ "res://.import/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/selector.svg b/addons/beehave/icons/selector.svg
new file mode 100644
index 0000000..01b107a
--- /dev/null
+++ b/addons/beehave/icons/selector.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/selector.svg.import b/addons/beehave/icons/selector.svg.import
new file mode 100644
index 0000000..05bed9c
--- /dev/null
+++ b/addons/beehave/icons/selector.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/selector.svg"
+dest_files=[ "res://.import/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/selector_star.svg b/addons/beehave/icons/selector_star.svg
new file mode 100644
index 0000000..e112337
--- /dev/null
+++ b/addons/beehave/icons/selector_star.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/addons/beehave/icons/selector_star.svg.import b/addons/beehave/icons/selector_star.svg.import
new file mode 100644
index 0000000..2b6ac98
--- /dev/null
+++ b/addons/beehave/icons/selector_star.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/selector_star.svg-368af7abfb9842c3f4258786871d1f03.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/selector_star.svg"
+dest_files=[ "res://.import/selector_star.svg-368af7abfb9842c3f4258786871d1f03.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=false
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/addons/beehave/icons/sequencer.svg b/addons/beehave/icons/sequencer.svg
new file mode 100644
index 0000000..1304ab0
--- /dev/null
+++ b/addons/beehave/icons/sequencer.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/sequencer.svg.import b/addons/beehave/icons/sequencer.svg.import
new file mode 100644
index 0000000..c4358f4
--- /dev/null
+++ b/addons/beehave/icons/sequencer.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/sequencer.svg-a7b0e1cc6b00c0067836f9a81d0b2f9b.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/sequencer.svg"
+dest_files=[ "res://.import/sequencer.svg-a7b0e1cc6b00c0067836f9a81d0b2f9b.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/sequencer_star.svg b/addons/beehave/icons/sequencer_star.svg
new file mode 100644
index 0000000..66efd5c
--- /dev/null
+++ b/addons/beehave/icons/sequencer_star.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/addons/beehave/icons/sequencer_star.svg.import b/addons/beehave/icons/sequencer_star.svg.import
new file mode 100644
index 0000000..6bfbf77
--- /dev/null
+++ b/addons/beehave/icons/sequencer_star.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/sequencer_star.svg-2385cbba0c38b4d4ec43e0996f8a3493.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/sequencer_star.svg"
+dest_files=[ "res://.import/sequencer_star.svg-2385cbba0c38b4d4ec43e0996f8a3493.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=false
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/addons/beehave/icons/succeed.svg b/addons/beehave/icons/succeed.svg
new file mode 100644
index 0000000..d8c5133
--- /dev/null
+++ b/addons/beehave/icons/succeed.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/succeed.svg.import b/addons/beehave/icons/succeed.svg.import
new file mode 100644
index 0000000..0b98563
--- /dev/null
+++ b/addons/beehave/icons/succeed.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/succeed.svg-c70f0a6f3d9b2864e4771942f0762307.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/succeed.svg"
+dest_files=[ "res://.import/succeed.svg-c70f0a6f3d9b2864e4771942f0762307.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/icons/tree.svg b/addons/beehave/icons/tree.svg
new file mode 100644
index 0000000..26dac9f
--- /dev/null
+++ b/addons/beehave/icons/tree.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/addons/beehave/icons/tree.svg.import b/addons/beehave/icons/tree.svg.import
new file mode 100644
index 0000000..028a5a1
--- /dev/null
+++ b/addons/beehave/icons/tree.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/tree.svg-c0b20ed88b2fe300c0296f7236049076.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/beehave/icons/tree.svg"
+dest_files=[ "res://.import/tree.svg-c0b20ed88b2fe300c0296f7236049076.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/beehave/nodes/beehave_node.gd b/addons/beehave/nodes/beehave_node.gd
new file mode 100644
index 0000000..d52e67b
--- /dev/null
+++ b/addons/beehave/nodes/beehave_node.gd
@@ -0,0 +1,8 @@
+extends BeehaveTree
+
+class_name BeehaveNode, "../icons/action.svg"
+
+enum { SUCCESS, FAILURE, RUNNING }
+
+func tick(actor, blackboard):
+ pass
diff --git a/addons/beehave/nodes/beehave_root.gd b/addons/beehave/nodes/beehave_root.gd
new file mode 100644
index 0000000..5201cc7
--- /dev/null
+++ b/addons/beehave/nodes/beehave_root.gd
@@ -0,0 +1,84 @@
+extends BeehaveTree
+
+class_name BeehaveRoot, "../icons/tree.svg"
+
+const Blackboard = preload("../blackboard.gd")
+const SUCCESS = 0
+const FAILURE = 1
+const RUNNING = 2
+
+enum ProcessMode {
+ PHYSICS_PROCESS,
+ IDLE,
+ MANUAL
+}
+
+export (ProcessMode) var process_mode = ProcessMode.PHYSICS_PROCESS setget set_process_mode
+export (bool) var enabled = true
+
+export (NodePath) var actor_node_path
+
+var actor : Node
+
+onready var blackboard = Blackboard.new()
+
+func _ready():
+ if self.get_child_count() != 1:
+ push_error("Beehave error: Root %s should have one child (NodePath: %s)" % [self.name, self.get_path()])
+ disable()
+ return
+
+ actor = get_parent()
+ if actor_node_path:
+ actor = get_node(actor_node_path)
+
+ set_process_mode(self.process_mode)
+
+func _process(delta):
+ tick(delta)
+
+func _physics_process(delta):
+ tick(delta)
+
+func tick(delta):
+ blackboard.set("delta", delta)
+
+ var status = self.get_child(0).tick(actor, blackboard)
+
+ if status != RUNNING:
+ blackboard.set("running_action", null)
+
+func get_running_action():
+ if blackboard.has("running_action"):
+ return blackboard.get("running_action")
+ return null
+
+func get_last_condition():
+ if blackboard.has("last_condition"):
+ return blackboard.get("last_condition")
+ return null
+
+func get_last_condition_status():
+ if blackboard.has("last_condition_status"):
+ var status = blackboard.get("last_condition_status")
+ if status == SUCCESS:
+ return "SUCCESS"
+ elif status == FAILURE:
+ return "FAILURE"
+ else:
+ return "RUNNING"
+ return ""
+
+func enable():
+ self.enabled = true
+ set_process_mode(self.process_mode)
+
+func disable():
+ self.enabled = false
+ set_process(self.enabled)
+ set_physics_process(self.enabled)
+
+func set_process_mode(value):
+ process_mode = value
+ set_process(process_mode == ProcessMode.IDLE)
+ set_physics_process(process_mode == ProcessMode.PHYSICS_PROCESS)
diff --git a/addons/beehave/nodes/beehave_tree.gd b/addons/beehave/nodes/beehave_tree.gd
new file mode 100644
index 0000000..9def663
--- /dev/null
+++ b/addons/beehave/nodes/beehave_tree.gd
@@ -0,0 +1,3 @@
+extends Node
+
+class_name BeehaveTree
diff --git a/addons/beehave/nodes/composites/composite.gd b/addons/beehave/nodes/composites/composite.gd
new file mode 100644
index 0000000..5a691f9
--- /dev/null
+++ b/addons/beehave/nodes/composites/composite.gd
@@ -0,0 +1,8 @@
+extends BeehaveNode
+
+class_name Composite, "../../icons/category_composite.svg"
+
+
+func _ready():
+ if self.get_child_count() < 1:
+ push_error("BehaviorTree Error: Composite %s should have at least one child (NodePath: %s)" % [self.name, self.get_path()])
diff --git a/addons/beehave/nodes/composites/selector.gd b/addons/beehave/nodes/composites/selector.gd
new file mode 100644
index 0000000..f78be66
--- /dev/null
+++ b/addons/beehave/nodes/composites/selector.gd
@@ -0,0 +1,18 @@
+extends Composite
+
+class_name SelectorComposite, "../../icons/selector.svg"
+
+func tick(actor, blackboard):
+ for c in get_children():
+ var response = c.tick(actor, blackboard)
+
+ if c is ConditionLeaf:
+ blackboard.set("last_condition", c)
+ blackboard.set("last_condition_status", response)
+
+ if response != FAILURE:
+ if c is ActionLeaf and response == RUNNING:
+ blackboard.set("running_action", c)
+ return response
+
+ return FAILURE
diff --git a/addons/beehave/nodes/composites/selector_star.gd b/addons/beehave/nodes/composites/selector_star.gd
new file mode 100644
index 0000000..0a272b5
--- /dev/null
+++ b/addons/beehave/nodes/composites/selector_star.gd
@@ -0,0 +1,33 @@
+# Special implementation of a selector that will
+# "wait" for running nodes and will not re-attempt
+# to execute previous nodes until that node is either
+# FAILED or SUCCEEDED
+extends Composite
+
+class_name SelectorStarComposite, "../../icons/selector_star.svg"
+
+
+var last_execution_index = 0
+
+func tick(actor, blackboard):
+ for c in get_children():
+ if c.get_index() < last_execution_index:
+ continue
+
+ var response = c.tick(actor, blackboard)
+
+ if c is ConditionLeaf:
+ blackboard.set("last_condition", c)
+ blackboard.set("last_condition_status", response)
+
+ if response != FAILURE:
+ if c is ActionLeaf and response == RUNNING:
+ blackboard.set("running_action", c)
+ if response == SUCCESS:
+ last_execution_index = 0
+ return response
+ else:
+ last_execution_index += 1
+
+ last_execution_index = 0
+ return FAILURE
diff --git a/addons/beehave/nodes/composites/sequence.gd b/addons/beehave/nodes/composites/sequence.gd
new file mode 100644
index 0000000..52d401d
--- /dev/null
+++ b/addons/beehave/nodes/composites/sequence.gd
@@ -0,0 +1,18 @@
+extends Composite
+
+class_name SequenceComposite, "../../icons/sequencer.svg"
+
+func tick(actor, blackboard):
+ for c in get_children():
+ var response = c.tick(actor, blackboard)
+
+ if c is ConditionLeaf:
+ blackboard.set("last_condition", c)
+ blackboard.set("last_condition_status", response)
+
+ if response != SUCCESS:
+ if c is ActionLeaf and response == RUNNING:
+ blackboard.set("running_action", c)
+ return response
+
+ return SUCCESS
diff --git a/addons/beehave/nodes/composites/sequence_star.gd b/addons/beehave/nodes/composites/sequence_star.gd
new file mode 100644
index 0000000..f0ae03f
--- /dev/null
+++ b/addons/beehave/nodes/composites/sequence_star.gd
@@ -0,0 +1,35 @@
+# Special implementation of sequencer who will execute
+# successful nodes only once until all nodes were successful
+
+extends Composite
+
+class_name SequenceStarComposite, "../../icons/sequencer_star.svg"
+
+var successful_index = 0
+
+func tick(actor, blackboard):
+ for c in get_children():
+ if c.get_index() < successful_index:
+ continue
+
+ var response = c.tick(actor, blackboard)
+
+ if c is ConditionLeaf:
+ blackboard.set("last_condition", c)
+ blackboard.set("last_condition_status", response)
+
+ if response != SUCCESS:
+ if response == FAILURE:
+ successful_index = 0
+ if c is ActionLeaf and response == RUNNING:
+ blackboard.set("running_action", c)
+ return response
+ else:
+ successful_index += 1
+
+ if successful_index == get_child_count():
+ successful_index = 0
+ return SUCCESS
+ else:
+ successful_index = 0
+ return FAILURE
diff --git a/addons/beehave/nodes/decorators/decorator.gd b/addons/beehave/nodes/decorators/decorator.gd
new file mode 100644
index 0000000..b0568d5
--- /dev/null
+++ b/addons/beehave/nodes/decorators/decorator.gd
@@ -0,0 +1,8 @@
+extends BeehaveNode
+
+class_name Decorator, "../../icons/category_decorator.svg"
+
+
+func _ready():
+ if self.get_child_count() != 1:
+ push_error("Beehave Error: Decorator %s should have only one child (NodePath: %s)" % [self.name, self.get_path()])
diff --git a/addons/beehave/nodes/decorators/failer.gd b/addons/beehave/nodes/decorators/failer.gd
new file mode 100644
index 0000000..15dfef4
--- /dev/null
+++ b/addons/beehave/nodes/decorators/failer.gd
@@ -0,0 +1,11 @@
+extends Decorator
+
+class_name AlwaysFailDecorator, "../../icons/fail.svg"
+
+
+func tick(action, blackboard):
+ for c in get_children():
+ var response = c.tick(action, blackboard)
+ if response == RUNNING:
+ return RUNNING
+ return FAILURE
diff --git a/addons/beehave/nodes/decorators/inverter.gd b/addons/beehave/nodes/decorators/inverter.gd
new file mode 100644
index 0000000..e1e70ea
--- /dev/null
+++ b/addons/beehave/nodes/decorators/inverter.gd
@@ -0,0 +1,17 @@
+extends Decorator
+
+class_name InverterDecorator, "../../icons/inverter.svg"
+
+
+func tick(action, blackboard):
+ for c in get_children():
+ var response = c.tick(action, blackboard)
+
+ if response == SUCCESS:
+ return FAILURE
+ if response == FAILURE:
+ return SUCCESS
+
+ if c is Leaf and response == RUNNING:
+ blackboard.set("running_action", c)
+ return RUNNING
diff --git a/addons/beehave/nodes/decorators/limiter.gd b/addons/beehave/nodes/decorators/limiter.gd
new file mode 100644
index 0000000..725f784
--- /dev/null
+++ b/addons/beehave/nodes/decorators/limiter.gd
@@ -0,0 +1,19 @@
+extends Decorator
+
+class_name LimiterDecorator, "../../icons/limiter.svg"
+
+onready var cache_key = 'limiter_%s' % self.get_instance_id()
+
+export (float) var max_count = 0
+
+func tick(actor, blackboard):
+ var current_count = blackboard.get(cache_key)
+
+ if current_count == null:
+ current_count = 0
+
+ if current_count <= max_count:
+ blackboard.set(cache_key, current_count + 1)
+ return self.get_child(0).tick(actor, blackboard)
+ else:
+ return FAILED
diff --git a/addons/beehave/nodes/decorators/succeeder.gd b/addons/beehave/nodes/decorators/succeeder.gd
new file mode 100644
index 0000000..1a7bacf
--- /dev/null
+++ b/addons/beehave/nodes/decorators/succeeder.gd
@@ -0,0 +1,11 @@
+extends Decorator
+
+class_name AlwaysSucceedDecorator, "../../icons/succeed.svg"
+
+
+func tick(action, blackboard):
+ for c in get_children():
+ var response = c.tick(action, blackboard)
+ if response == RUNNING:
+ return RUNNING
+ return SUCCESS
diff --git a/addons/beehave/nodes/leaves/action.gd b/addons/beehave/nodes/leaves/action.gd
new file mode 100644
index 0000000..a30041d
--- /dev/null
+++ b/addons/beehave/nodes/leaves/action.gd
@@ -0,0 +1,3 @@
+extends Leaf
+
+class_name ActionLeaf, "../../icons/action.svg"
diff --git a/addons/beehave/nodes/leaves/condition.gd b/addons/beehave/nodes/leaves/condition.gd
new file mode 100644
index 0000000..d91ff37
--- /dev/null
+++ b/addons/beehave/nodes/leaves/condition.gd
@@ -0,0 +1,3 @@
+extends Leaf
+
+class_name ConditionLeaf, "../../icons/condition.svg"
diff --git a/addons/beehave/nodes/leaves/leaf.gd b/addons/beehave/nodes/leaves/leaf.gd
new file mode 100644
index 0000000..503fa1b
--- /dev/null
+++ b/addons/beehave/nodes/leaves/leaf.gd
@@ -0,0 +1,3 @@
+extends BeehaveNode
+
+class_name Leaf, "../../icons/action.svg"
diff --git a/addons/beehave/plugin.cfg b/addons/beehave/plugin.cfg
new file mode 100644
index 0000000..7a1b19b
--- /dev/null
+++ b/addons/beehave/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="Beehave"
+description="🐝 Behaviour Tree addon for Godot Engine"
+author="bitbrain"
+version="1.2.0"
+script="plugin.gd"
diff --git a/addons/beehave/plugin.gd b/addons/beehave/plugin.gd
new file mode 100644
index 0000000..d670e1b
--- /dev/null
+++ b/addons/beehave/plugin.gd
@@ -0,0 +1,5 @@
+tool
+extends EditorPlugin
+
+func _init():
+ print("Beehave initialized!")
diff --git a/project.godot b/project.godot
index 9db388f..207342a 100644
--- a/project.godot
+++ b/project.godot
@@ -8,6 +8,106 @@
config_version=4
+_global_script_classes=[ {
+"base": "Leaf",
+"class": "ActionLeaf",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/leaves/action.gd"
+}, {
+"base": "Decorator",
+"class": "AlwaysFailDecorator",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/decorators/failer.gd"
+}, {
+"base": "Decorator",
+"class": "AlwaysSucceedDecorator",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/decorators/succeeder.gd"
+}, {
+"base": "BeehaveTree",
+"class": "BeehaveNode",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/beehave_node.gd"
+}, {
+"base": "BeehaveTree",
+"class": "BeehaveRoot",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/beehave_root.gd"
+}, {
+"base": "Node",
+"class": "BeehaveTree",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/beehave_tree.gd"
+}, {
+"base": "BeehaveNode",
+"class": "Composite",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/composites/composite.gd"
+}, {
+"base": "Leaf",
+"class": "ConditionLeaf",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/leaves/condition.gd"
+}, {
+"base": "BeehaveNode",
+"class": "Decorator",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/decorators/decorator.gd"
+}, {
+"base": "Decorator",
+"class": "InverterDecorator",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/decorators/inverter.gd"
+}, {
+"base": "BeehaveNode",
+"class": "Leaf",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/leaves/leaf.gd"
+}, {
+"base": "Decorator",
+"class": "LimiterDecorator",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/decorators/limiter.gd"
+}, {
+"base": "Composite",
+"class": "SelectorComposite",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/composites/selector.gd"
+}, {
+"base": "Composite",
+"class": "SelectorStarComposite",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/composites/selector_star.gd"
+}, {
+"base": "Composite",
+"class": "SequenceComposite",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/composites/sequence.gd"
+}, {
+"base": "Composite",
+"class": "SequenceStarComposite",
+"language": "GDScript",
+"path": "res://addons/beehave/nodes/composites/sequence_star.gd"
+} ]
+_global_script_class_icons={
+"ActionLeaf": "res://addons/beehave/icons/action.svg",
+"AlwaysFailDecorator": "res://addons/beehave/icons/fail.svg",
+"AlwaysSucceedDecorator": "res://addons/beehave/icons/succeed.svg",
+"BeehaveNode": "res://addons/beehave/icons/action.svg",
+"BeehaveRoot": "res://addons/beehave/icons/tree.svg",
+"BeehaveTree": "",
+"Composite": "res://addons/beehave/icons/category_composite.svg",
+"ConditionLeaf": "res://addons/beehave/icons/condition.svg",
+"Decorator": "res://addons/beehave/icons/category_decorator.svg",
+"InverterDecorator": "res://addons/beehave/icons/inverter.svg",
+"Leaf": "res://addons/beehave/icons/action.svg",
+"LimiterDecorator": "res://addons/beehave/icons/limiter.svg",
+"SelectorComposite": "res://addons/beehave/icons/selector.svg",
+"SelectorStarComposite": "res://addons/beehave/icons/selector_star.svg",
+"SequenceComposite": "res://addons/beehave/icons/sequencer.svg",
+"SequenceStarComposite": "res://addons/beehave/icons/sequencer_star.svg"
+}
+
[application]
config/name="City Limits"
@@ -32,7 +132,7 @@ window/stretch/aspect="keep"
[editor_plugins]
-enabled=PoolStringArray( )
+enabled=PoolStringArray( "res://addons/beehave/plugin.cfg" )
[gdnative]
@@ -66,8 +166,8 @@ ui_down={
}
ui_cheats={
"deadzone": 0.5,
-"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":false,"meta":true,"command":true,"pressed":false,"scancode":67,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":true,"command":true,"pressed":false,"scancode":88,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":true,"meta":true,"command":true,"pressed":false,"scancode":67,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":true,"command":true,"pressed":false,"scancode":88,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}