commit 960c39730326be6749ff42e827c20c8fa21a8e7f Author: TsubakiLoL <2789646812@qq.com> Date: Fri Oct 25 15:41:39 2024 +0800 add diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/TEST/mystic_woods_free_2.2/read_me.txt b/TEST/mystic_woods_free_2.2/read_me.txt new file mode 100644 index 0000000..ed05e65 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/read_me.txt @@ -0,0 +1,13 @@ +Howdy! Thank you for downloading the Mystic Woods asset pack. + +This is an ongoing project that I will be adding content to over time, so let me know if there's anything you'd like for me to create. + +License - Free Version + - You can only use these assets in non-commercial projects. + - You can modify the assets. + - You can not redistribute or resale, even if modified. + +Follow me on Twitter for updates on all of my projects. +https://twitter.com/GameEndeavor + +If you enjoy this then leave a rating and comment. It helps to support this project! \ No newline at end of file diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/README.txt b/TEST/mystic_woods_free_2.2/sprites/characters/README.txt new file mode 100644 index 0000000..edebaa5 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/characters/README.txt @@ -0,0 +1,18 @@ +Player and skeleton sprites are on a 48x48 grid. +Slime is on a 32x32 grid. + +Flip right facing sprites to get the left facing sprites. + +Animations [rows (0 based for us programmers)] +Player: +[0 - 2] idle +[3 - 5] move +[6 - 8] attack +[9] death + +Enemies: +[0 - 2] idle +[3 - 5] move +[6 - 8] attack +[9 - 11] damaged +[12] death \ No newline at end of file diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/player.png b/TEST/mystic_woods_free_2.2/sprites/characters/player.png new file mode 100644 index 0000000..b22f4b3 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/characters/player.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/player.png.import b/TEST/mystic_woods_free_2.2/sprites/characters/player.png.import new file mode 100644 index 0000000..66f2bc6 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/characters/player.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b8mr80al21rka" +path="res://.godot/imported/player.png-6c5d9809679900d37526f87878eeca81.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/characters/player.png" +dest_files=["res://.godot/imported/player.png-6c5d9809679900d37526f87878eeca81.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png new file mode 100644 index 0000000..763b6ee Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png.import b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png.import new file mode 100644 index 0000000..09a29f8 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://csh4bp80n0jqu" +path="res://.godot/imported/skeleton.png-c741bdfc3b3bceef37fc96f2409dde34.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/characters/skeleton.png" +dest_files=["res://.godot/imported/skeleton.png-c741bdfc3b3bceef37fc96f2409dde34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png new file mode 100644 index 0000000..3dbe1e2 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png.import b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png.import new file mode 100644 index 0000000..9ad6c2c --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qg24llaiojac" +path="res://.godot/imported/skeleton_swordless.png-cb67559e57d51e44b259e3220ec81f18.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/characters/skeleton_swordless.png" +dest_files=["res://.godot/imported/skeleton_swordless.png-cb67559e57d51e44b259e3220ec81f18.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/slime.png b/TEST/mystic_woods_free_2.2/sprites/characters/slime.png new file mode 100644 index 0000000..3b5dc54 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/characters/slime.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/characters/slime.png.import b/TEST/mystic_woods_free_2.2/sprites/characters/slime.png.import new file mode 100644 index 0000000..456755c --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/characters/slime.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cs24y4rmilkg6" +path="res://.godot/imported/slime.png-017622d32381f7a679938c4e151998ab.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/characters/slime.png" +dest_files=["res://.godot/imported/slime.png-017622d32381f7a679938c4e151998ab.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png b/TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png new file mode 100644 index 0000000..d799879 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png.import new file mode 100644 index 0000000..310639e --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bgow2a3rs7q4j" +path="res://.godot/imported/chest_01.png-c2e99bfd7e7e892ed7f8651520769f4f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/chest_01.png" +dest_files=["res://.godot/imported/chest_01.png-c2e99bfd7e7e892ed7f8651520769f4f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png b/TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png new file mode 100644 index 0000000..361b386 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png.import new file mode 100644 index 0000000..8f494bd --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5yvqfk63mcq8" +path="res://.godot/imported/chest_02.png-b6c6487f808784e97570961d0c4dfc50.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/chest_02.png" +dest_files=["res://.godot/imported/chest_02.png-b6c6487f808784e97570961d0c4dfc50.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/objects.png b/TEST/mystic_woods_free_2.2/sprites/objects/objects.png new file mode 100644 index 0000000..53175d6 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/objects.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/objects.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/objects.png.import new file mode 100644 index 0000000..076541c --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/objects.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ox0pbgoe4gpa" +path="res://.godot/imported/objects.png-88a8f30761719fe7ea83996f3040f370.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/objects.png" +dest_files=["res://.godot/imported/objects.png-88a8f30761719fe7ea83996f3040f370.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png new file mode 100644 index 0000000..5c9d12e Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png.import new file mode 100644 index 0000000..b390ce0 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hsr83f2epk1x" +path="res://.godot/imported/rock_in_water-sheet.png-494a098f01fbbb8d4eeab900b8b7f91b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water-sheet.png" +dest_files=["res://.godot/imported/rock_in_water-sheet.png-494a098f01fbbb8d4eeab900b8b7f91b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png new file mode 100644 index 0000000..5c9d12e Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png.import new file mode 100644 index 0000000..ed75811 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vt57eibv22qt" +path="res://.godot/imported/rock_in_water_01-sheet.png-7c5ed78fe97440039700ee7e4305fcf1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01-sheet.png" +dest_files=["res://.godot/imported/rock_in_water_01-sheet.png-7c5ed78fe97440039700ee7e4305fcf1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png new file mode 100644 index 0000000..6aeb07c Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png.import new file mode 100644 index 0000000..c5e6e2f --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cp73joxb56lep" +path="res://.godot/imported/rock_in_water_01.png-d49d566ddad27e087af789f831d1a4b5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_01.png" +dest_files=["res://.godot/imported/rock_in_water_01.png-d49d566ddad27e087af789f831d1a4b5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png new file mode 100644 index 0000000..38d10a5 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png.import new file mode 100644 index 0000000..738cc03 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cw0fk2o5sdl5k" +path="res://.godot/imported/rock_in_water_02.png-532b7ae0b3325cc350e0ef0275c8a8cd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_02.png" +dest_files=["res://.godot/imported/rock_in_water_02.png-532b7ae0b3325cc350e0ef0275c8a8cd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png new file mode 100644 index 0000000..aba065c Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png.import new file mode 100644 index 0000000..92be37e --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mr47gn76hwg7" +path="res://.godot/imported/rock_in_water_03.png-af0df681573d1e19c87e766443c69a92.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_03.png" +dest_files=["res://.godot/imported/rock_in_water_03.png-af0df681573d1e19c87e766443c69a92.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png new file mode 100644 index 0000000..79ba4ea Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png.import new file mode 100644 index 0000000..302bb4e --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7sxt5h40wjd8" +path="res://.godot/imported/rock_in_water_04.png-ba1e6f9c526e81869be3c6c84a5ff2ab.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_04.png" +dest_files=["res://.godot/imported/rock_in_water_04.png-ba1e6f9c526e81869be3c6c84a5ff2ab.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png new file mode 100644 index 0000000..3b5e385 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png.import new file mode 100644 index 0000000..25b76ad --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ck44rro1efn4c" +path="res://.godot/imported/rock_in_water_05.png-084ad4b304e8e3fa1e175b817b48e09b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_05.png" +dest_files=["res://.godot/imported/rock_in_water_05.png-084ad4b304e8e3fa1e175b817b48e09b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png new file mode 100644 index 0000000..38d10a5 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png.import b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png.import new file mode 100644 index 0000000..358c69a --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kdvr8cdq1qtp" +path="res://.godot/imported/rock_in_water_06.png-f9656dc55bf81469416ee3cf6ecc193b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/objects/rock_in_water_06.png" +dest_files=["res://.godot/imported/rock_in_water_06.png-f9656dc55bf81469416ee3cf6ecc193b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png b/TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png new file mode 100644 index 0000000..85398a5 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png.import b/TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png.import new file mode 100644 index 0000000..5acfdaa --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d38emyi3bc20w" +path="res://.godot/imported/dust_particles_01.png-a6f4ecc279a7be030480bfba7330f796.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/particles/dust_particles_01.png" +dest_files=["res://.godot/imported/dust_particles_01.png-a6f4ecc279a7be030480bfba7330f796.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png new file mode 100644 index 0000000..db267ed Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png.import new file mode 100644 index 0000000..14488b5 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://84gy53ru31tj" +path="res://.godot/imported/decor_16x16.png-ebc18fc236a98a70e81737810ca73cd1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/decor_16x16.png" +dest_files=["res://.godot/imported/decor_16x16.png-ebc18fc236a98a70e81737810ca73cd1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png new file mode 100644 index 0000000..8b91ef7 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png.import new file mode 100644 index 0000000..e1b606a --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c66txlefa0g1m" +path="res://.godot/imported/decor_8x8.png-8afa989d76666e299ba1bfa26b472e2e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/decor_8x8.png" +dest_files=["res://.godot/imported/decor_8x8.png-8afa989d76666e299ba1bfa26b472e2e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png new file mode 100644 index 0000000..b1c7856 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png.import new file mode 100644 index 0000000..2c29d25 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqkj0y6pww0bc" +path="res://.godot/imported/fences.png-b9d16689c79f4ae187aad63d1e5a4689.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/fences.png" +dest_files=["res://.godot/imported/fences.png-b9d16689c79f4ae187aad63d1e5a4689.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png new file mode 100644 index 0000000..8532f3d Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png.import new file mode 100644 index 0000000..d5cfef9 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://madjrbxrxb18" +path="res://.godot/imported/carpet.png-c7c1c7527f7abaa3bc874c3c2fc72934.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/floors/carpet.png" +dest_files=["res://.godot/imported/carpet.png-c7c1c7527f7abaa3bc874c3c2fc72934.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png new file mode 100644 index 0000000..2a4bfb8 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png.import new file mode 100644 index 0000000..3fd907e --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdhd8cgpqqtqd" +path="res://.godot/imported/flooring.png-43d7f9ac4b16312ab64770bc17144840.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/floors/flooring.png" +dest_files=["res://.godot/imported/flooring.png-43d7f9ac4b16312ab64770bc17144840.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png new file mode 100644 index 0000000..f2989f3 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png.import new file mode 100644 index 0000000..29fcf85 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://t16xm66rao0u" +path="res://.godot/imported/wooden.png-d8637089a4903542d1c41347324bdeec.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/floors/wooden.png" +dest_files=["res://.godot/imported/wooden.png-d8637089a4903542d1c41347324bdeec.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png new file mode 100644 index 0000000..6fbef63 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png.import new file mode 100644 index 0000000..eda986f --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://deevu4iws0aul" +path="res://.godot/imported/grass.png-b38d151724ddf2b6325fcfc9c526874c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/grass.png" +dest_files=["res://.godot/imported/grass.png-b38d151724ddf2b6325fcfc9c526874c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png new file mode 100644 index 0000000..e91002d Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png.import new file mode 100644 index 0000000..38526cc --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://br51oca80dq3i" +path="res://.godot/imported/plains.png-8fc6377fe63b8d212a1ec687887b3df5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/plains.png" +dest_files=["res://.godot/imported/plains.png-8fc6377fe63b8d212a1ec687887b3df5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png new file mode 100644 index 0000000..d6c79f8 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png.import new file mode 100644 index 0000000..4d731f2 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ds5gmt2oas2j3" +path="res://.godot/imported/walls.png-2920d9fb288c762e49098bb246c0cd22.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/walls/walls.png" +dest_files=["res://.godot/imported/walls.png-2920d9fb288c762e49098bb246c0cd22.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png new file mode 100644 index 0000000..a7db1a9 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png.import new file mode 100644 index 0000000..104085f --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://eobrojwn66mi" +path="res://.godot/imported/wooden_door.png-abc43f7cd3766c96e8f1fb22023e6b11.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door.png" +dest_files=["res://.godot/imported/wooden_door.png-abc43f7cd3766c96e8f1fb22023e6b11.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png new file mode 100644 index 0000000..ad9fdfc Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png.import new file mode 100644 index 0000000..f4b3100 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cerem13mn5ucr" +path="res://.godot/imported/wooden_door_b.png-b154ee80d2fc97a5c2957eb07c6f615b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/walls/wooden_door_b.png" +dest_files=["res://.godot/imported/wooden_door_b.png-b154ee80d2fc97a5c2957eb07c6f615b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png new file mode 100644 index 0000000..1ee035c Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png.import new file mode 100644 index 0000000..26b4e4d --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://pg743cc5uv6g" +path="res://.godot/imported/water-sheet.png-97e1bd4345ba3193b9b0d736d3624019.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water-sheet.png" +dest_files=["res://.godot/imported/water-sheet.png-97e1bd4345ba3193b9b0d736d3624019.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png new file mode 100644 index 0000000..8dcfa5b Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png.import new file mode 100644 index 0000000..25f3953 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b740fvavkjn44" +path="res://.godot/imported/water1.png-6365ebf5cadbe6d647db816d6d87b5f2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water1.png" +dest_files=["res://.godot/imported/water1.png-6365ebf5cadbe6d647db816d6d87b5f2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png new file mode 100644 index 0000000..f164d6a Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png.import new file mode 100644 index 0000000..e23a04b --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djtnlgm0k15fg" +path="res://.godot/imported/water2.png-36a91ccfcca65406ba32a4f7f919f0e5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water2.png" +dest_files=["res://.godot/imported/water2.png-36a91ccfcca65406ba32a4f7f919f0e5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png new file mode 100644 index 0000000..f7872ae Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png.import new file mode 100644 index 0000000..8e45467 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkku4jpjmesbb" +path="res://.godot/imported/water3.png-6fb7cc1c2a7f82ba36d70bae328646c7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water3.png" +dest_files=["res://.godot/imported/water3.png-6fb7cc1c2a7f82ba36d70bae328646c7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png new file mode 100644 index 0000000..4373c39 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png.import new file mode 100644 index 0000000..18c57ce --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dy5xp1e2kd156" +path="res://.godot/imported/water4.png-828fd72c261e630dc9cce3b5693f77c8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water4.png" +dest_files=["res://.godot/imported/water4.png-828fd72c261e630dc9cce3b5693f77c8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png new file mode 100644 index 0000000..f164d6a Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png.import new file mode 100644 index 0000000..9100798 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ccf3j70kvu4rx" +path="res://.godot/imported/water5.png-e40c6a26d825c2c93b0d25a7f3fef30a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water5.png" +dest_files=["res://.godot/imported/water5.png-e40c6a26d825c2c93b0d25a7f3fef30a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png new file mode 100644 index 0000000..4465224 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png.import new file mode 100644 index 0000000..e8f5a39 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dk5lb6o5iypcv" +path="res://.godot/imported/water6.png-ba003e892683f429157a8b6b2a450ec2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water6.png" +dest_files=["res://.godot/imported/water6.png-ba003e892683f429157a8b6b2a450ec2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png new file mode 100644 index 0000000..3cacb04 Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png.import new file mode 100644 index 0000000..a119da6 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vhtvtjpb6lgx" +path="res://.godot/imported/water_decorations.png-41081b5d89150170a462622f47f0db92.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water_decorations.png" +dest_files=["res://.godot/imported/water_decorations.png-41081b5d89150170a462622f47f0db92.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png new file mode 100644 index 0000000..f16513c Binary files /dev/null and b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png differ diff --git a/TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png.import b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png.import new file mode 100644 index 0000000..8d1f514 --- /dev/null +++ b/TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b52dx8ej5geti" +path="res://.godot/imported/water_lillies.png-505badebc331d33e5ec126469a676a7a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://TEST/mystic_woods_free_2.2/sprites/tilesets/water_lillies.png" +dest_files=["res://.godot/imported/water_lillies.png-505badebc331d33e5ec126469a676a7a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/beehave/LICENSE b/addons/beehave/LICENSE new file mode 100644 index 0000000..caabbff --- /dev/null +++ b/addons/beehave/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 bitbrain + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/beehave/blackboard.gd b/addons/beehave/blackboard.gd new file mode 100644 index 0000000..3948212 --- /dev/null +++ b/addons/beehave/blackboard.gd @@ -0,0 +1,51 @@ +@icon("icons/blackboard.svg") +class_name Blackboard extends Node + +const DEFAULT = "default" + +## The blackboard is an object that can be used to store and access data between +## multiple nodes of the behavior tree. +@export var blackboard: Dictionary = {}: + set(b): + blackboard = b + _data[DEFAULT] = blackboard + +var _data: Dictionary = {} + + +func _ready(): + _data[DEFAULT] = blackboard + + +func keys() -> Array[String]: + var keys: Array[String] + keys.assign(_data.keys().duplicate()) + return keys + + +func set_value(key: Variant, value: Variant, blackboard_name: String = DEFAULT) -> void: + if not _data.has(blackboard_name): + _data[blackboard_name] = {} + + _data[blackboard_name][key] = value + + +func get_value( + key: Variant, default_value: Variant = null, blackboard_name: String = DEFAULT +) -> Variant: + if has_value(key, blackboard_name): + return _data[blackboard_name].get(key, default_value) + return default_value + + +func has_value(key: Variant, blackboard_name: String = DEFAULT) -> bool: + return ( + _data.has(blackboard_name) + and _data[blackboard_name].has(key) + and _data[blackboard_name][key] != null + ) + + +func erase_value(key: Variant, blackboard_name: String = DEFAULT) -> void: + if _data.has(blackboard_name): + _data[blackboard_name][key] = null diff --git a/addons/beehave/debug/debugger.gd b/addons/beehave/debug/debugger.gd new file mode 100644 index 0000000..73bdc50 --- /dev/null +++ b/addons/beehave/debug/debugger.gd @@ -0,0 +1,96 @@ +@tool +extends EditorDebuggerPlugin + +const DebuggerTab := preload("debugger_tab.gd") +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + +var debugger_tab := DebuggerTab.new() +var floating_window: Window +var session: EditorDebuggerSession + + +func _has_capture(prefix: String) -> bool: + return prefix == "beehave" + + +func _capture(message: String, data: Array, session_id: int) -> bool: + # in case the behavior tree has invalid setup this might be null + if debugger_tab == null: + return false + + if message == "beehave:register_tree": + debugger_tab.register_tree(data[0]) + return true + if message == "beehave:unregister_tree": + debugger_tab.unregister_tree(data[0]) + return true + if message == "beehave:process_tick": + debugger_tab.graph.process_tick(data[0], data[1]) + return true + if message == "beehave:process_begin": + debugger_tab.graph.process_begin(data[0]) + return true + if message == "beehave:process_end": + debugger_tab.graph.process_end(data[0]) + return true + return false + + +func _setup_session(session_id: int) -> void: + session = get_session(session_id) + session.started.connect(debugger_tab.start) + session.stopped.connect(debugger_tab.stop) + + debugger_tab.name = "🐝 Beehave" + debugger_tab.make_floating.connect(_on_make_floating) + debugger_tab.session = session + session.add_session_tab(debugger_tab) + + +func _on_make_floating() -> void: + var plugin := BeehaveUtils.get_plugin() + if not plugin: + return + if floating_window: + _on_window_close_requested() + return + + var border_size := Vector2(4, 4) * BeehaveUtils.get_editor_scale() + var editor_interface: EditorInterface = plugin.get_editor_interface() + var editor_main_screen = editor_interface.get_editor_main_screen() + debugger_tab.get_parent().remove_child(debugger_tab) + + floating_window = Window.new() + + var panel := Panel.new() + panel.add_theme_stylebox_override( + "panel", + editor_interface.get_base_control().get_theme_stylebox("PanelForeground", "EditorStyles") + ) + panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + floating_window.add_child(panel) + + var margin := MarginContainer.new() + margin.add_child(debugger_tab) + margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + margin.add_theme_constant_override("margin_right", border_size.x) + margin.add_theme_constant_override("margin_left", border_size.x) + margin.add_theme_constant_override("margin_top", border_size.y) + margin.add_theme_constant_override("margin_bottom", border_size.y) + panel.add_child(margin) + + floating_window.title = "🐝 Beehave" + floating_window.wrap_controls = true + floating_window.min_size = Vector2i(600, 350) + floating_window.size = debugger_tab.size + floating_window.position = editor_main_screen.global_position + floating_window.transient = true + floating_window.close_requested.connect(_on_window_close_requested) + editor_interface.get_base_control().add_child(floating_window) + + +func _on_window_close_requested() -> void: + debugger_tab.get_parent().remove_child(debugger_tab) + session.add_session_tab(debugger_tab) + floating_window.queue_free() + floating_window = null diff --git a/addons/beehave/debug/debugger_messages.gd b/addons/beehave/debug/debugger_messages.gd new file mode 100644 index 0000000..6e0a055 --- /dev/null +++ b/addons/beehave/debug/debugger_messages.gd @@ -0,0 +1,30 @@ +class_name BeehaveDebuggerMessages + + +static func can_send_message() -> bool: + return not Engine.is_editor_hint() and OS.has_feature("editor") + + +static func register_tree(beehave_tree: Dictionary) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:register_tree", [beehave_tree]) + + +static func unregister_tree(instance_id: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:unregister_tree", [instance_id]) + + +static func process_tick(instance_id: int, status: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:process_tick", [instance_id, status]) + + +static func process_begin(instance_id: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:process_begin", [instance_id]) + + +static func process_end(instance_id: int) -> void: + if can_send_message(): + EngineDebugger.send_message("beehave:process_end", [instance_id]) diff --git a/addons/beehave/debug/debugger_tab.gd b/addons/beehave/debug/debugger_tab.gd new file mode 100644 index 0000000..758d8b6 --- /dev/null +++ b/addons/beehave/debug/debugger_tab.gd @@ -0,0 +1,125 @@ +@tool +class_name BeehaveDebuggerTab extends PanelContainer + +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + +signal make_floating + +const OldBeehaveGraphEdit := preload("old_graph_edit.gd") +const NewBeehaveGraphEdit := preload("new_graph_edit.gd") + +const TREE_ICON := preload("../icons/tree.svg") + +var graph +var container: HSplitContainer +var item_list: ItemList +var message: Label + +var active_trees: Dictionary +var active_tree_id: int = -1 +var session: EditorDebuggerSession + + +func _ready() -> void: + container = HSplitContainer.new() + add_child(container) + + item_list = ItemList.new() + item_list.custom_minimum_size = Vector2(200, 0) + item_list.item_selected.connect(_on_item_selected) + container.add_child(item_list) + if Engine.get_version_info().minor >= 2: + graph = NewBeehaveGraphEdit.new(BeehaveUtils.get_frames()) + else: + graph = OldBeehaveGraphEdit.new(BeehaveUtils.get_frames()) + + container.add_child(graph) + + message = Label.new() + message.text = "Run Project for debugging" + message.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + message.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + message.set_anchors_preset(Control.PRESET_CENTER) + add_child(message) + + var button := Button.new() + button.flat = true + button.name = "MakeFloatingButton" + button.icon = get_theme_icon(&"ExternalLink", &"EditorIcons") + button.pressed.connect(func(): make_floating.emit()) + button.tooltip_text = "Make floating" + button.focus_mode = Control.FOCUS_NONE + graph.get_menu_container().add_child(button) + + var toggle_button := Button.new() + toggle_button.flat = true + toggle_button.name = "TogglePanelButton" + toggle_button.icon = get_theme_icon(&"Back", &"EditorIcons") + toggle_button.pressed.connect(_on_toggle_button_pressed.bind(toggle_button)) + toggle_button.tooltip_text = "Toggle Panel" + toggle_button.focus_mode = Control.FOCUS_NONE + graph.get_menu_container().add_child(toggle_button) + graph.get_menu_container().move_child(toggle_button, 0) + + stop() + visibility_changed.connect(_on_visibility_changed) + + +func start() -> void: + container.visible = true + message.visible = false + + +func stop() -> void: + container.visible = false + message.visible = true + + active_trees.clear() + item_list.clear() + graph.beehave_tree = {} + + +func register_tree(data: Dictionary) -> void: + if not active_trees.has(data.id): + var idx := item_list.add_item(data.name, TREE_ICON) + item_list.set_item_tooltip(idx, data.path) + item_list.set_item_metadata(idx, data.id) + + active_trees[data.id] = data + + if active_tree_id == data.id.to_int(): + graph.beehave_tree = data + + +func unregister_tree(instance_id: int) -> void: + var id := str(instance_id) + for i in item_list.item_count: + if item_list.get_item_metadata(i) == id: + item_list.remove_item(i) + break + + active_trees.erase(id) + + if graph.beehave_tree.get("id", "") == id: + graph.beehave_tree = {} + + +func _on_toggle_button_pressed(toggle_button: Button) -> void: + item_list.visible = !item_list.visible + toggle_button.icon = get_theme_icon( + &"Back" if item_list.visible else &"Forward", &"EditorIcons" + ) + + +func _on_item_selected(idx: int) -> void: + var id: StringName = item_list.get_item_metadata(idx) + graph.beehave_tree = active_trees.get(id, {}) + + active_tree_id = id.to_int() + if session != null: + session.send_message("beehave:activate_tree", [active_tree_id]) + + +func _on_visibility_changed() -> void: + if session != null: + session.send_message("beehave:visibility_changed", [visible and is_visible_in_tree()]) diff --git a/addons/beehave/debug/global_debugger.gd b/addons/beehave/debug/global_debugger.gd new file mode 100644 index 0000000..7350b3e --- /dev/null +++ b/addons/beehave/debug/global_debugger.gd @@ -0,0 +1,38 @@ +extends Node + +var _registered_trees: Dictionary +var _active_tree + + +func _enter_tree() -> void: + EngineDebugger.register_message_capture("beehave", _on_debug_message) + + +func _on_debug_message(message: String, data: Array) -> bool: + if message == "activate_tree": + _set_active_tree(data[0]) + return true + if message == "visibility_changed": + if _active_tree && is_instance_valid(_active_tree): + _active_tree._can_send_message = data[0] + return true + return false + + +func _set_active_tree(tree_id: int) -> void: + var tree = _registered_trees.get(tree_id, null) + if not tree: + return + + if _active_tree && is_instance_valid(_active_tree): + _active_tree._can_send_message = false + _active_tree = tree + _active_tree._can_send_message = true + + +func register_tree(tree) -> void: + _registered_trees[tree.get_instance_id()] = tree + + +func unregister_tree(tree) -> void: + _registered_trees.erase(tree.get_instance_id()) diff --git a/addons/beehave/debug/icons/horizontal_layout.svg b/addons/beehave/debug/icons/horizontal_layout.svg new file mode 100644 index 0000000..235dc62 --- /dev/null +++ b/addons/beehave/debug/icons/horizontal_layout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/beehave/debug/icons/horizontal_layout.svg.import b/addons/beehave/debug/icons/horizontal_layout.svg.import new file mode 100644 index 0000000..539e518 --- /dev/null +++ b/addons/beehave/debug/icons/horizontal_layout.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bah77esichnyx" +path="res://.godot/imported/horizontal_layout.svg-d2a7af351e44f9bf61d0c938b6f47fac.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/horizontal_layout.svg" +dest_files=["res://.godot/imported/horizontal_layout.svg-d2a7af351e44f9bf61d0c938b6f47fac.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_bottom.svg b/addons/beehave/debug/icons/port_bottom.svg new file mode 100644 index 0000000..3ce70e7 --- /dev/null +++ b/addons/beehave/debug/icons/port_bottom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_bottom.svg.import b/addons/beehave/debug/icons/port_bottom.svg.import new file mode 100644 index 0000000..8845c5b --- /dev/null +++ b/addons/beehave/debug/icons/port_bottom.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://da3b236rjbqns" +path="res://.godot/imported/port_bottom.svg-e5c5c61b642a79ab9c2b66ff56603d34.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_bottom.svg" +dest_files=["res://.godot/imported/port_bottom.svg-e5c5c61b642a79ab9c2b66ff56603d34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_left.svg b/addons/beehave/debug/icons/port_left.svg new file mode 100644 index 0000000..c1f6717 --- /dev/null +++ b/addons/beehave/debug/icons/port_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_left.svg.import b/addons/beehave/debug/icons/port_left.svg.import new file mode 100644 index 0000000..7ea9827 --- /dev/null +++ b/addons/beehave/debug/icons/port_left.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnufc8p6spdtn" +path="res://.godot/imported/port_left.svg-69cd927c4db555f1edbb8d1f553ea2fd.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_left.svg" +dest_files=["res://.godot/imported/port_left.svg-69cd927c4db555f1edbb8d1f553ea2fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_right.svg b/addons/beehave/debug/icons/port_right.svg new file mode 100644 index 0000000..2560af5 --- /dev/null +++ b/addons/beehave/debug/icons/port_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_right.svg.import b/addons/beehave/debug/icons/port_right.svg.import new file mode 100644 index 0000000..20931cd --- /dev/null +++ b/addons/beehave/debug/icons/port_right.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bbmd6vk23ympm" +path="res://.godot/imported/port_right.svg-f760bd8be2dd613d0d3848c998c92a2a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_right.svg" +dest_files=["res://.godot/imported/port_right.svg-f760bd8be2dd613d0d3848c998c92a2a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/port_top.svg b/addons/beehave/debug/icons/port_top.svg new file mode 100644 index 0000000..d3b99e1 --- /dev/null +++ b/addons/beehave/debug/icons/port_top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/beehave/debug/icons/port_top.svg.import b/addons/beehave/debug/icons/port_top.svg.import new file mode 100644 index 0000000..dec7820 --- /dev/null +++ b/addons/beehave/debug/icons/port_top.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bw8wmxdfom8eh" +path="res://.godot/imported/port_top.svg-d1b336cdc6a0dd570305782a1e56f61d.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/port_top.svg" +dest_files=["res://.godot/imported/port_top.svg-d1b336cdc6a0dd570305782a1e56f61d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/icons/vertical_layout.svg b/addons/beehave/debug/icons/vertical_layout.svg new file mode 100644 index 0000000..d59ffb0 --- /dev/null +++ b/addons/beehave/debug/icons/vertical_layout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/addons/beehave/debug/icons/vertical_layout.svg.import b/addons/beehave/debug/icons/vertical_layout.svg.import new file mode 100644 index 0000000..8ddcfca --- /dev/null +++ b/addons/beehave/debug/icons/vertical_layout.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpyxu6i1dx5qh" +path="res://.godot/imported/vertical_layout.svg-1a08fee4b09812a05bcf3defb8afcc4c.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/debug/icons/vertical_layout.svg" +dest_files=["res://.godot/imported/vertical_layout.svg-1a08fee4b09812a05bcf3defb8afcc4c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/debug/new_frames.gd b/addons/beehave/debug/new_frames.gd new file mode 100644 index 0000000..4b739fd --- /dev/null +++ b/addons/beehave/debug/new_frames.gd @@ -0,0 +1,69 @@ +@tool +extends RefCounted + + +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + + +const SUCCESS_COLOR := Color("#07783a") +const NORMAL_COLOR := Color("#15181e") +const FAILURE_COLOR := Color("#82010b") +const RUNNING_COLOR := Color("#c29c06") + +var panel_normal: StyleBoxFlat +var panel_success: StyleBoxFlat +var panel_failure: StyleBoxFlat +var panel_running: StyleBoxFlat + +var titlebar_normal: StyleBoxFlat +var titlebar_success: StyleBoxFlat +var titlebar_failure: StyleBoxFlat +var titlebar_running: StyleBoxFlat + + +func _init() -> void: + var plugin := BeehaveUtils.get_plugin() + if not plugin: + return + + + titlebar_normal = ( + plugin + .get_editor_interface() + .get_base_control() + .get_theme_stylebox(&"titlebar", &"GraphNode")\ + .duplicate() + ) + titlebar_success = titlebar_normal.duplicate() + titlebar_failure = titlebar_normal.duplicate() + titlebar_running = titlebar_normal.duplicate() + + titlebar_success.bg_color = SUCCESS_COLOR + titlebar_failure.bg_color = FAILURE_COLOR + titlebar_running.bg_color = RUNNING_COLOR + + titlebar_success.border_color = SUCCESS_COLOR + titlebar_failure.border_color = FAILURE_COLOR + titlebar_running.border_color = RUNNING_COLOR + + + panel_normal = ( + plugin + .get_editor_interface() + .get_base_control() + .get_theme_stylebox(&"panel", &"GraphNode") + .duplicate() + ) + panel_success = ( + plugin + .get_editor_interface() + .get_base_control() + .get_theme_stylebox(&"panel_selected", &"GraphNode") + .duplicate() + ) + panel_failure = panel_success.duplicate() + panel_running = panel_success.duplicate() + + panel_success.border_color = SUCCESS_COLOR + panel_failure.border_color = FAILURE_COLOR + panel_running.border_color = RUNNING_COLOR diff --git a/addons/beehave/debug/new_graph_edit.gd b/addons/beehave/debug/new_graph_edit.gd new file mode 100644 index 0000000..71161ab --- /dev/null +++ b/addons/beehave/debug/new_graph_edit.gd @@ -0,0 +1,296 @@ +@tool +extends GraphEdit + +const BeehaveGraphNode := preload("new_graph_node.gd") + +const HORIZONTAL_LAYOUT_ICON := preload("icons/horizontal_layout.svg") +const VERTICAL_LAYOUT_ICON := preload("icons/vertical_layout.svg") + +const PROGRESS_SHIFT: int = 50 +const INACTIVE_COLOR: Color = Color("#898989") +const ACTIVE_COLOR: Color = Color("#c29c06") +const SUCCESS_COLOR: Color = Color("#07783a") + + +var updating_graph: bool = false +var arraging_nodes: bool = false +var beehave_tree: Dictionary: + set(value): + if beehave_tree == value: + return + beehave_tree = value + active_nodes.clear() + _update_graph() + +var horizontal_layout: bool = false: + set(value): + if updating_graph or arraging_nodes: + return + if horizontal_layout == value: + return + horizontal_layout = value + _update_layout_button() + _update_graph() + + +var frames:RefCounted +var active_nodes: Array[String] +var progress: int = 0 +var layout_button: Button + + +func _init(frames:RefCounted) -> void: + self.frames = frames + + +func _ready() -> void: + custom_minimum_size = Vector2(100, 300) + set("show_arrange_button", true) + minimap_enabled = false + layout_button = Button.new() + layout_button.flat = true + layout_button.focus_mode = Control.FOCUS_NONE + layout_button.pressed.connect(func(): horizontal_layout = not horizontal_layout) + get_menu_container().add_child(layout_button) + _update_layout_button() + + +func _update_graph() -> void: + if updating_graph: + return + + updating_graph = true + + clear_connections() + + for child in _get_child_nodes(): + remove_child(child) + child.queue_free() + + if not beehave_tree.is_empty(): + _add_nodes(beehave_tree) + _connect_nodes(beehave_tree) + _arrange_nodes.call_deferred(beehave_tree) + + updating_graph = false + + +func _add_nodes(node: Dictionary) -> void: + if node.is_empty(): + return + var gnode := BeehaveGraphNode.new(frames, horizontal_layout) + add_child(gnode) + gnode.title_text = node.name + gnode.name = node.id + gnode.icon = _get_icon(node.type.back()) + + if node.type.has(&"BeehaveTree"): + gnode.set_slots(false, true) + elif node.type.has(&"Leaf"): + gnode.set_slots(true, false) + elif node.type.has(&"Composite") or node.type.has(&"Decorator"): + gnode.set_slots(true, true) + + for child in node.get("children", []): + _add_nodes(child) + + +func _connect_nodes(node: Dictionary) -> void: + for child in node.get("children", []): + connect_node(node.id, 0, child.id, 0) + _connect_nodes(child) + + +func _arrange_nodes(node: Dictionary) -> void: + if arraging_nodes: + return + + arraging_nodes = true + + var tree_node := _create_tree_nodes(node) + tree_node.update_positions(horizontal_layout) + _place_nodes(tree_node) + + arraging_nodes = false + + +func _create_tree_nodes(node: Dictionary, root: TreeNode = null) -> TreeNode: + var tree_node := TreeNode.new(get_node(node.id), root) + for child in node.get("children", []): + var child_node := _create_tree_nodes(child, tree_node) + tree_node.children.push_back(child_node) + return tree_node + + +func _place_nodes(node: TreeNode) -> void: + node.item.position_offset = Vector2(node.x, node.y) + for child in node.children: + _place_nodes(child) + + +func _get_icon(type: StringName) -> Texture2D: + var classes := ProjectSettings.get_global_class_list() + for c in classes: + if c["class"] == type: + var icon_path := c.get("icon", String()) + if not icon_path.is_empty(): + return load(icon_path) + return null + + +func get_menu_container() -> Control: + return call("get_menu_hbox") + + +func get_status(status: int) -> String: + if status == 0: + return "SUCCESS" + elif status == 1: + return "FAILURE" + return "RUNNING" + + +func process_begin(instance_id: int) -> void: + if not _is_same_tree(instance_id): + return + + for child in _get_child_nodes(): + child.set_meta("status", -1) + + +func process_tick(instance_id: int, status: int) -> void: + var node := get_node_or_null(str(instance_id)) + if node: + node.text = "Status: %s" % get_status(status) + node.set_status(status) + node.set_meta("status", status) + if status == 0 or status == 2: + if not active_nodes.has(node.name): + active_nodes.push_back(node.name) + + +func process_end(instance_id: int) -> void: + if not _is_same_tree(instance_id): + return + + for child in _get_child_nodes(): + var status := child.get_meta("status", -1) + match status: + 0: + active_nodes.erase(child.name) + child.set_color(SUCCESS_COLOR) + 1: + active_nodes.erase(child.name) + child.set_color(INACTIVE_COLOR) + 2: + child.set_color(ACTIVE_COLOR) + _: + child.text = " " + child.set_status(status) + child.set_color(INACTIVE_COLOR) + + +func _is_same_tree(instance_id: int) -> bool: + return str(instance_id) == beehave_tree.get("id", "") + + +func _get_child_nodes() -> Array[Node]: + return get_children().filter(func(child): return child is BeehaveGraphNode) + + +func _get_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array: + for child in _get_child_nodes(): + for port in child.get_input_port_count(): + if not (child.position_offset + child.get_input_port_position(port)).is_equal_approx(to_position): + continue + to_position = child.position_offset + child.get_custom_input_port_position(horizontal_layout) + for port in child.get_output_port_count(): + if not (child.position_offset + child.get_output_port_position(port)).is_equal_approx(from_position): + continue + from_position = child.position_offset + child.get_custom_output_port_position(horizontal_layout) + return _get_elbow_connection_line(from_position, to_position) + + +func _get_elbow_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array: + var points: PackedVector2Array + + points.push_back(from_position) + + var mid_position := ((to_position + from_position) / 2).round() + if horizontal_layout: + points.push_back(Vector2(mid_position.x, from_position.y)) + points.push_back(Vector2(mid_position.x, to_position.y)) + else: + points.push_back(Vector2(from_position.x, mid_position.y)) + points.push_back(Vector2(to_position.x, mid_position.y)) + + points.push_back(to_position) + + return points + + +func _process(delta: float) -> void: + if not active_nodes.is_empty(): + progress += 10 if delta >= 0.05 else 1 + if progress >= 1000: + progress = 0 + queue_redraw() + + +func _draw() -> void: + if active_nodes.is_empty(): + return + + var circle_size: float = max(3, 6 * zoom) + var progress_shift: float = PROGRESS_SHIFT * zoom + + var connections := get_connection_list() + for c in connections: + var from_node: StringName + var to_node: StringName + + from_node = c.from_node + to_node = c.to_node + + if not from_node in active_nodes or not c.to_node in active_nodes: + continue + + var from := get_node(String(from_node)) + var to := get_node(String(to_node)) + + if from.get_meta("status", -1) < 0 or to.get_meta("status", -1) < 0: + return + + var output_port_position: Vector2 + var input_port_position: Vector2 + + var scale_factor: float = from.get_rect().size.x / from.size.x + + var line := _get_elbow_connection_line( + from.position + from.get_custom_output_port_position(horizontal_layout) * scale_factor, + to.position + to.get_custom_input_port_position(horizontal_layout) * scale_factor + ) + + var curve = Curve2D.new() + for l in line: + curve.add_point(l) + + var max_steps := int(curve.get_baked_length()) + var current_shift := progress % max_steps + var p := curve.sample_baked(current_shift) + draw_circle(p, circle_size, ACTIVE_COLOR) + + var shift := current_shift - progress_shift + while shift >= 0: + draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) + shift -= progress_shift + + shift = current_shift + progress_shift + while shift <= curve.get_baked_length(): + draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) + shift += progress_shift + + +func _update_layout_button() -> void: + layout_button.icon = VERTICAL_LAYOUT_ICON if horizontal_layout else HORIZONTAL_LAYOUT_ICON + layout_button.tooltip_text = "Switch to Vertical layout" if horizontal_layout else "Switch to Horizontal layout" diff --git a/addons/beehave/debug/new_graph_node.gd b/addons/beehave/debug/new_graph_node.gd new file mode 100644 index 0000000..70f06dc --- /dev/null +++ b/addons/beehave/debug/new_graph_node.gd @@ -0,0 +1,155 @@ +@tool +extends GraphNode + + +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + +const PORT_TOP_ICON := preload("icons/port_top.svg") +const PORT_BOTTOM_ICON := preload("icons/port_bottom.svg") +const PORT_LEFT_ICON := preload("icons/port_left.svg") +const PORT_RIGHT_ICON := preload("icons/port_right.svg") + + +@export var title_text: String: + set(value): + title_text = value + if title_label: + title_label.text = value + +@export var text: String: + set(value): + text = value + if label: + label.text = " " if text.is_empty() else text + +@export var icon: Texture2D: + set(value): + icon = value + if icon_rect: + icon_rect.texture = value + +var layout_size: float: + get: + return size.y if horizontal else size.x + + +var icon_rect: TextureRect +var title_label: Label +var label: Label +var titlebar_hbox: HBoxContainer + +var frames: RefCounted +var horizontal: bool = false + + +func _init(frames:RefCounted, horizontal: bool = false) -> void: + self.frames = frames + self.horizontal = horizontal + + +func _ready() -> void: + custom_minimum_size = Vector2(50, 50) * BeehaveUtils.get_editor_scale() + draggable = false + + add_theme_color_override("close_color", Color.TRANSPARENT) + add_theme_icon_override("close", ImageTexture.new()) + + # For top port + var top_port: Control = Control.new() + add_child(top_port) + + icon_rect = TextureRect.new() + icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + + titlebar_hbox = get_titlebar_hbox() + titlebar_hbox.get_child(0).queue_free() + titlebar_hbox.alignment = BoxContainer.ALIGNMENT_BEGIN + titlebar_hbox.add_child(icon_rect) + + title_label = Label.new() + title_label.add_theme_color_override("font_color", Color.WHITE) + var title_font: Font = get_theme_font("title_font").duplicate() + if title_font is FontVariation: + title_font.variation_embolden = 1 + elif title_font is FontFile: + title_font.font_weight = 700 + title_label.add_theme_font_override("font", title_font) + title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + title_label.text = title_text + titlebar_hbox.add_child(title_label) + + label = Label.new() + label.text = " " if text.is_empty() else text + add_child(label) + + # For bottom port + add_child(Control.new()) + + minimum_size_changed.connect(_on_size_changed) + _on_size_changed.call_deferred() + + +func _draw_port(slot_index: int, port_position: Vector2i, left: bool, color: Color) -> void: + if horizontal: + if is_slot_enabled_left(1): + draw_texture(PORT_LEFT_ICON, Vector2(0, size.y / 2) + Vector2(-4, -5), color) + if is_slot_enabled_right(1): + draw_texture(PORT_RIGHT_ICON, Vector2(size.x, size.y / 2) + Vector2(-5, -4.5), color) + else: + if slot_index == 0 and is_slot_enabled_left(0): + draw_texture(PORT_TOP_ICON, Vector2(size.x / 2, 0) + Vector2(-4.5, -7), color) + elif slot_index == 1: + draw_texture(PORT_BOTTOM_ICON, Vector2(size.x / 2, size.y) + Vector2(-4.5, -5), color) + + +func get_custom_input_port_position(horizontal: bool) -> Vector2: + if horizontal: + return Vector2(0, size.y / 2) + else: + return Vector2(size.x/2, 0) + + +func get_custom_output_port_position(horizontal: bool) -> Vector2: + if horizontal: + return Vector2(size.x, size.y / 2) + else: + return Vector2(size.x / 2, size.y) + + +func set_status(status: int) -> void: + match status: + 0: _set_stylebox_overrides(frames.panel_success, frames.titlebar_success) + 1: _set_stylebox_overrides(frames.panel_failure, frames.titlebar_failure) + 2: _set_stylebox_overrides(frames.panel_running, frames.titlebar_running) + _: _set_stylebox_overrides(frames.panel_normal, frames.titlebar_normal) + + +func set_slots(left_enabled: bool, right_enabled: bool) -> void: + if horizontal: + set_slot(1, left_enabled, -1, Color.WHITE, right_enabled, -1, Color.WHITE, PORT_LEFT_ICON, PORT_RIGHT_ICON) + else: + set_slot(0, left_enabled, -1, Color.WHITE, false, -1, Color.TRANSPARENT, PORT_TOP_ICON, null) + set_slot(2, false, -1, Color.TRANSPARENT, right_enabled, -1, Color.WHITE, null, PORT_BOTTOM_ICON) + + +func set_color(color: Color) -> void: + set_input_color(color) + set_output_color(color) + + +func set_input_color(color: Color) -> void: + set_slot_color_left(1 if horizontal else 0, color) + + +func set_output_color(color: Color) -> void: + set_slot_color_right(1 if horizontal else 2, color) + + +func _set_stylebox_overrides(panel_stylebox: StyleBox, titlebar_stylebox: StyleBox) -> void: + add_theme_stylebox_override("panel", panel_stylebox) + add_theme_stylebox_override("titlebar", titlebar_stylebox) + + +func _on_size_changed(): + add_theme_constant_override("port_offset", 12 * BeehaveUtils.get_editor_scale() if horizontal else round(size.x)) diff --git a/addons/beehave/debug/old_frames.gd b/addons/beehave/debug/old_frames.gd new file mode 100644 index 0000000..a9f6aa8 --- /dev/null +++ b/addons/beehave/debug/old_frames.gd @@ -0,0 +1,47 @@ +@tool +extends RefCounted + +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + +const SUCCESS_COLOR := Color("#009944c8") +const NORMAL_COLOR := Color("#15181e") +const FAILURE_COLOR := Color("#cf000f80") +const RUNNING_COLOR := Color("#ffcc00c8") + +var empty: StyleBoxEmpty +var normal: StyleBoxFlat +var success: StyleBoxFlat +var failure: StyleBoxFlat +var running: StyleBoxFlat + + +func _init() -> void: + var plugin := BeehaveUtils.get_plugin() + if not plugin: + return + + var editor_scale := BeehaveUtils.get_editor_scale() + + empty = StyleBoxEmpty.new() + + normal = ( + plugin + . get_editor_interface() + . get_base_control() + . get_theme_stylebox(&"frame", &"GraphNode") + . duplicate() + ) + + success = ( + plugin + . get_editor_interface() + . get_base_control() + . get_theme_stylebox(&"selected_frame", &"GraphNode") + . duplicate() + ) + failure = success.duplicate() + running = success.duplicate() + + success.border_color = SUCCESS_COLOR + failure.border_color = FAILURE_COLOR + running.border_color = RUNNING_COLOR diff --git a/addons/beehave/debug/old_graph_edit.gd b/addons/beehave/debug/old_graph_edit.gd new file mode 100644 index 0000000..bae64c8 --- /dev/null +++ b/addons/beehave/debug/old_graph_edit.gd @@ -0,0 +1,286 @@ +@tool +extends GraphEdit + +const BeehaveGraphNode := preload("old_graph_node.gd") + +const HORIZONTAL_LAYOUT_ICON := preload("icons/horizontal_layout.svg") +const VERTICAL_LAYOUT_ICON := preload("icons/vertical_layout.svg") + +const PROGRESS_SHIFT: int = 50 +const INACTIVE_COLOR: Color = Color("#898989aa") +const ACTIVE_COLOR: Color = Color("#ffcc00c8") +const SUCCESS_COLOR: Color = Color("#009944c8") + +var updating_graph: bool = false +var arraging_nodes: bool = false +var beehave_tree: Dictionary: + set(value): + if beehave_tree == value: + return + beehave_tree = value + active_nodes.clear() + _update_graph() + +var horizontal_layout: bool = false: + set(value): + if updating_graph or arraging_nodes: + return + if horizontal_layout == value: + return + horizontal_layout = value + _update_layout_button() + _update_graph() + +var frames: RefCounted +var active_nodes: Array[String] +var progress: int = 0 +var layout_button: Button + + +func _init(frames: RefCounted) -> void: + self.frames = frames + + +func _ready() -> void: + custom_minimum_size = Vector2(100, 300) + set("arrange_nodes_button_hidden", true) + minimap_enabled = false + layout_button = Button.new() + layout_button.flat = true + layout_button.focus_mode = Control.FOCUS_NONE + layout_button.pressed.connect(func(): horizontal_layout = not horizontal_layout) + get_menu_container().add_child(layout_button) + _update_layout_button() + + +func _update_graph() -> void: + if updating_graph: + return + + updating_graph = true + + clear_connections() + + for child in _get_child_nodes(): + remove_child(child) + child.queue_free() + + if not beehave_tree.is_empty(): + _add_nodes(beehave_tree) + _connect_nodes(beehave_tree) + _arrange_nodes.call_deferred(beehave_tree) + + updating_graph = false + + +func _add_nodes(node: Dictionary) -> void: + if node.is_empty(): + return + var gnode := BeehaveGraphNode.new(frames, horizontal_layout) + add_child(gnode) + gnode.title_text = node.name + gnode.name = node.id + gnode.icon = _get_icon(node.type.back()) + + if node.type.has(&"BeehaveTree"): + gnode.set_slots(false, true) + elif node.type.has(&"Leaf"): + gnode.set_slots(true, false) + elif node.type.has(&"Composite") or node.type.has(&"Decorator"): + gnode.set_slots(true, true) + + for child in node.get("children", []): + _add_nodes(child) + + +func _connect_nodes(node: Dictionary) -> void: + for child in node.get("children", []): + connect_node(node.id, 0, child.id, 0) + _connect_nodes(child) + + +func _arrange_nodes(node: Dictionary) -> void: + if arraging_nodes: + return + + arraging_nodes = true + + var tree_node := _create_tree_nodes(node) + tree_node.update_positions(horizontal_layout) + _place_nodes(tree_node) + + arraging_nodes = false + + +func _create_tree_nodes(node: Dictionary, root: TreeNode = null) -> TreeNode: + var tree_node := TreeNode.new(get_node(node.id), root) + for child in node.get("children", []): + var child_node := _create_tree_nodes(child, tree_node) + tree_node.children.push_back(child_node) + return tree_node + + +func _place_nodes(node: TreeNode) -> void: + node.item.position_offset = Vector2(node.x, node.y) + for child in node.children: + _place_nodes(child) + + +func _get_icon(type: StringName) -> Texture2D: + var classes := ProjectSettings.get_global_class_list() + for c in classes: + if c["class"] == type: + var icon_path := c.get("icon", String()) + if not icon_path.is_empty(): + return load(icon_path) + return null + + +func get_menu_container() -> Control: + return call("get_zoom_hbox") + + +func get_status(status: int) -> String: + if status == 0: + return "SUCCESS" + elif status == 1: + return "FAILURE" + return "RUNNING" + + +func process_begin(instance_id: int) -> void: + if not _is_same_tree(instance_id): + return + + for child in _get_child_nodes(): + child.set_meta("status", -1) + + +func process_tick(instance_id: int, status: int) -> void: + var node := get_node_or_null(str(instance_id)) + if node: + node.text = "Status: %s" % get_status(status) + node.set_status(status) + node.set_meta("status", status) + if status == 0 or status == 2: + if not active_nodes.has(node.name): + active_nodes.push_back(node.name) + + +func process_end(instance_id: int) -> void: + if not _is_same_tree(instance_id): + return + + for child in _get_child_nodes(): + var status := child.get_meta("status", -1) + match status: + 0: + active_nodes.erase(child.name) + child.set_color(SUCCESS_COLOR) + 1: + active_nodes.erase(child.name) + child.set_color(INACTIVE_COLOR) + 2: + child.set_color(ACTIVE_COLOR) + _: + child.text = " " + child.set_status(status) + child.set_color(INACTIVE_COLOR) + + +func _is_same_tree(instance_id: int) -> bool: + return str(instance_id) == beehave_tree.get("id", "") + + +func _get_child_nodes() -> Array[Node]: + return get_children().filter(func(child): return child is BeehaveGraphNode) + + +func _get_connection_line(from_position: Vector2, to_position: Vector2) -> PackedVector2Array: + var points: PackedVector2Array + + from_position = from_position.round() + to_position = to_position.round() + + points.push_back(from_position) + + var mid_position := ((to_position + from_position) / 2).round() + if horizontal_layout: + points.push_back(Vector2(mid_position.x, from_position.y)) + points.push_back(Vector2(mid_position.x, to_position.y)) + else: + points.push_back(Vector2(from_position.x, mid_position.y)) + points.push_back(Vector2(to_position.x, mid_position.y)) + + points.push_back(to_position) + + return points + + +func _process(delta: float) -> void: + if not active_nodes.is_empty(): + progress += 10 if delta >= 0.05 else 1 + if progress >= 1000: + progress = 0 + queue_redraw() + + +func _draw() -> void: + if active_nodes.is_empty(): + return + + var circle_size: float = max(3, 6 * zoom) + var progress_shift: float = PROGRESS_SHIFT * zoom + + var connections := get_connection_list() + for c in connections: + var from_node: StringName + var to_node: StringName + + from_node = c.from + to_node = c.to + + if not from_node in active_nodes or not c.to_node in active_nodes: + continue + + var from := get_node(String(from_node)) + var to := get_node(String(to_node)) + + if from.get_meta("status", -1) < 0 or to.get_meta("status", -1) < 0: + return + + var output_port_position: Vector2 + var input_port_position: Vector2 + + output_port_position = ( + from.position + from.call("get_connection_output_position", c.from_port) + ) + input_port_position = to.position + to.call("get_connection_input_position", c.to_port) + + var line := _get_connection_line(output_port_position, input_port_position) + + var curve = Curve2D.new() + for l in line: + curve.add_point(l) + + var max_steps := int(curve.get_baked_length()) + var current_shift := progress % max_steps + var p := curve.sample_baked(current_shift) + draw_circle(p, circle_size, ACTIVE_COLOR) + + var shift := current_shift - progress_shift + while shift >= 0: + draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) + shift -= progress_shift + + shift = current_shift + progress_shift + while shift <= curve.get_baked_length(): + draw_circle(curve.sample_baked(shift), circle_size, ACTIVE_COLOR) + shift += progress_shift + + +func _update_layout_button() -> void: + layout_button.icon = VERTICAL_LAYOUT_ICON if horizontal_layout else HORIZONTAL_LAYOUT_ICON + layout_button.tooltip_text = ( + "Switch to Vertical layout" if horizontal_layout else "Switch to Horizontal layout" + ) diff --git a/addons/beehave/debug/old_graph_node.gd b/addons/beehave/debug/old_graph_node.gd new file mode 100644 index 0000000..a0a978b --- /dev/null +++ b/addons/beehave/debug/old_graph_node.gd @@ -0,0 +1,166 @@ +@tool +extends GraphNode + +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + +const DEFAULT_COLOR := Color("#dad4cb") + +const PORT_TOP_ICON := preload("icons/port_top.svg") +const PORT_BOTTOM_ICON := preload("icons/port_bottom.svg") +const PORT_LEFT_ICON := preload("icons/port_left.svg") +const PORT_RIGHT_ICON := preload("icons/port_right.svg") + +@export var title_text: String: + set(value): + title_text = value + if title_label: + title_label.text = value + +@export var text: String: + set(value): + text = value + if label: + label.text = " " if text.is_empty() else text + +@export var icon: Texture2D: + set(value): + icon = value + if icon_rect: + icon_rect.texture = value + +var layout_size: float: + get: + return size.y if horizontal else size.x + +var panel: PanelContainer +var icon_rect: TextureRect +var title_label: Label +var container: VBoxContainer +var label: Label + +var frames: RefCounted +var horizontal: bool = false + + +func _init(frames: RefCounted, horizontal: bool = false) -> void: + self.frames = frames + self.horizontal = horizontal + + +func _ready() -> void: + custom_minimum_size = Vector2(50, 50) * BeehaveUtils.get_editor_scale() + draggable = false + + add_theme_stylebox_override("frame", frames.empty if frames != null else null) + add_theme_stylebox_override("selected_frame", frames.empty if frames != null else null) + add_theme_color_override("close_color", Color.TRANSPARENT) + add_theme_icon_override("close", ImageTexture.new()) + + # For top port + add_child(Control.new()) + + panel = PanelContainer.new() + panel.mouse_filter = Control.MOUSE_FILTER_PASS + panel.add_theme_stylebox_override("panel", frames.normal if frames != null else null) + add_child(panel) + + var vbox_container := VBoxContainer.new() + panel.add_child(vbox_container) + + var title_size := 24 * BeehaveUtils.get_editor_scale() + var margin_container := MarginContainer.new() + margin_container.add_theme_constant_override( + "margin_top", -title_size - 2 * BeehaveUtils.get_editor_scale() + ) + margin_container.mouse_filter = Control.MOUSE_FILTER_PASS + vbox_container.add_child(margin_container) + + var title_container := HBoxContainer.new() + title_container.add_child(Control.new()) + title_container.mouse_filter = Control.MOUSE_FILTER_PASS + title_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + margin_container.add_child(title_container) + + icon_rect = TextureRect.new() + icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + title_container.add_child(icon_rect) + + title_label = Label.new() + title_label.add_theme_color_override("font_color", DEFAULT_COLOR) + title_label.add_theme_font_override("font", get_theme_font("title_font")) + title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + title_label.text = title_text + title_container.add_child(title_label) + + title_container.add_child(Control.new()) + + container = VBoxContainer.new() + container.size_flags_vertical = Control.SIZE_EXPAND_FILL + container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + panel.add_child(container) + + label = Label.new() + label.text = " " if text.is_empty() else text + container.add_child(label) + + # For bottom port + add_child(Control.new()) + + minimum_size_changed.connect(_on_size_changed) + _on_size_changed.call_deferred() + + +func set_status(status: int) -> void: + panel.add_theme_stylebox_override("panel", _get_stylebox(status)) + + +func _get_stylebox(status: int) -> StyleBox: + match status: + 0: + return frames.success + 1: + return frames.failure + 2: + return frames.running + _: + return frames.normal + + +func set_slots(left_enabled: bool, right_enabled: bool) -> void: + if horizontal: + set_slot( + 1, + left_enabled, + 0, + Color.WHITE, + right_enabled, + 0, + Color.WHITE, + PORT_LEFT_ICON, + PORT_RIGHT_ICON + ) + else: + set_slot(0, left_enabled, 0, Color.WHITE, false, -2, Color.TRANSPARENT, PORT_TOP_ICON, null) + set_slot( + 2, false, -1, Color.TRANSPARENT, right_enabled, 0, Color.WHITE, null, PORT_BOTTOM_ICON + ) + + +func set_color(color: Color) -> void: + set_input_color(color) + set_output_color(color) + + +func set_input_color(color: Color) -> void: + set_slot_color_left(1 if horizontal else 0, color) + + +func set_output_color(color: Color) -> void: + set_slot_color_right(1 if horizontal else 2, color) + + +func _on_size_changed(): + add_theme_constant_override( + "port_offset", 12 * BeehaveUtils.get_editor_scale() if horizontal else round(size.x / 2.0) + ) diff --git a/addons/beehave/debug/tree_node.gd b/addons/beehave/debug/tree_node.gd new file mode 100644 index 0000000..1377970 --- /dev/null +++ b/addons/beehave/debug/tree_node.gd @@ -0,0 +1,275 @@ +class_name TreeNode +extends RefCounted + +# Based on https://rachel53461.wordpress.com/2014/04/20/algorithm-for-drawing-trees/ + +const SIBLING_DISTANCE: float = 20.0 +const LEVEL_DISTANCE: float = 40.0 + +const BeehaveUtils := preload("res://addons/beehave/utils/utils.gd") + +var x: float +var y: float +var mod: float +var parent: TreeNode +var children: Array[TreeNode] + +var item: GraphNode + + +func _init(p_item: GraphNode = null, p_parent: TreeNode = null) -> void: + parent = p_parent + item = p_item + + +func is_leaf() -> bool: + return children.is_empty() + + +func is_most_left() -> bool: + if not parent: + return true + return parent.children.front() == self + + +func is_most_right() -> bool: + if not parent: + return true + return parent.children.back() == self + + +func get_previous_sibling() -> TreeNode: + if not parent or is_most_left(): + return null + return parent.children[parent.children.find(self) - 1] + + +func get_next_sibling() -> TreeNode: + if not parent or is_most_right(): + return null + return parent.children[parent.children.find(self) + 1] + + +func get_most_left_sibling() -> TreeNode: + if not parent: + return null + + if is_most_left(): + return self + + return parent.children.front() + + +func get_most_left_child() -> TreeNode: + if children.is_empty(): + return null + return children.front() + + +func get_most_right_child() -> TreeNode: + if children.is_empty(): + return null + return children.back() + + +func update_positions(horizontally: bool = false) -> void: + _initialize_nodes(self, 0) + _calculate_initial_x(self) + + _check_all_children_on_screen(self) + _calculate_final_positions(self, 0) + + if horizontally: + _swap_x_y(self) + _calculate_x(self, 0) + else: + _calculate_y(self, 0) + + +func _initialize_nodes(node: TreeNode, depth: int) -> void: + node.x = -1 + node.y = depth + node.mod = 0 + + for child in node.children: + _initialize_nodes(child, depth + 1) + + +func _calculate_initial_x(node: TreeNode) -> void: + for child in node.children: + _calculate_initial_x(child) + if node.is_leaf(): + if not node.is_most_left(): + node.x = ( + node.get_previous_sibling().x + + node.get_previous_sibling().item.layout_size + + SIBLING_DISTANCE + ) + else: + node.x = 0 + else: + var mid: float + if node.children.size() == 1: + var offset: float = (node.children.front().item.layout_size - node.item.layout_size) / 2 + mid = node.children.front().x + offset + else: + var left_child := node.get_most_left_child() + var right_child := node.get_most_right_child() + mid = ( + ( + left_child.x + + right_child.x + + right_child.item.layout_size + - node.item.layout_size + ) + / 2 + ) + + if node.is_most_left(): + node.x = mid + else: + node.x = ( + node.get_previous_sibling().x + + node.get_previous_sibling().item.layout_size + + SIBLING_DISTANCE + ) + node.mod = node.x - mid + + if not node.is_leaf() and not node.is_most_left(): + _check_for_conflicts(node) + + +func _calculate_final_positions(node: TreeNode, mod_sum: float) -> void: + node.x += mod_sum + mod_sum += node.mod + + for child in node.children: + _calculate_final_positions(child, mod_sum) + + +func _check_all_children_on_screen(node: TreeNode) -> void: + var node_contour: Dictionary = {} + _get_left_contour(node, 0, node_contour) + + var shift_amount: float = 0 + for y in node_contour.keys(): + if node_contour[y] + shift_amount < 0: + shift_amount = (node_contour[y] * -1) + + if shift_amount > 0: + node.x += shift_amount + node.mod += shift_amount + + +func _check_for_conflicts(node: TreeNode) -> void: + var min_distance := SIBLING_DISTANCE + var shift_value: float = 0 + var shift_sibling: TreeNode = null + + var node_contour: Dictionary = {} # { int, float } + _get_left_contour(node, 0, node_contour) + + var sibling := node.get_most_left_sibling() + while sibling != null and sibling != node: + var sibling_contour: Dictionary = {} + _get_right_contour(sibling, 0, sibling_contour) + + for level in range( + node.y + 1, min(sibling_contour.keys().max(), node_contour.keys().max()) + 1 + ): + var distance: float = node_contour[level] - sibling_contour[level] + if distance + shift_value < min_distance: + shift_value = min_distance - distance + shift_sibling = sibling + + sibling = sibling.get_next_sibling() + + if shift_value > 0: + node.x += shift_value + node.mod += shift_value + _center_nodes_between(shift_sibling, node) + + +func _center_nodes_between(left_node: TreeNode, right_node: TreeNode) -> void: + var left_index := left_node.parent.children.find(left_node) + var right_index := left_node.parent.children.find(right_node) + + var num_nodes_between: int = (right_index - left_index) - 1 + if num_nodes_between > 0: + # The extra distance that needs to be split into num_nodes_between + 1 + # in order to find the new node spacing so that nodes are equally spaced + var distance_to_allocate: float = right_node.x - left_node.x - left_node.item.layout_size + # Subtract sizes on nodes in between + for i in range(left_index + 1, right_index): + distance_to_allocate -= left_node.parent.children[i].item.layout_size + # Divide space equally + var distance_between_nodes: float = distance_to_allocate / (num_nodes_between + 1) + + var prev_node := left_node + var middle_node := left_node.get_next_sibling() + while middle_node != right_node: + var desire_x: float = prev_node.x + prev_node.item.layout_size + distance_between_nodes + var offset := desire_x - middle_node.x + middle_node.x += offset + middle_node.mod += offset + prev_node = middle_node + middle_node = middle_node.get_next_sibling() + + +func _get_left_contour(node: TreeNode, mod_sum: float, values: Dictionary) -> void: + var node_left: float = node.x + mod_sum + var depth := int(node.y) + if not values.has(depth): + values[depth] = node_left + else: + values[depth] = min(values[depth], node_left) + + mod_sum += node.mod + for child in node.children: + _get_left_contour(child, mod_sum, values) + + +func _get_right_contour(node: TreeNode, mod_sum: float, values: Dictionary) -> void: + var node_right: float = node.x + mod_sum + node.item.layout_size + var depth := int(node.y) + if not values.has(depth): + values[depth] = node_right + else: + values[depth] = max(values[depth], node_right) + + mod_sum += node.mod + for child in node.children: + _get_right_contour(child, mod_sum, values) + + +func _swap_x_y(node: TreeNode) -> void: + for child in node.children: + _swap_x_y(child) + + var temp := node.x + node.x = node.y + node.y = temp + + +func _calculate_x(node: TreeNode, offset: int) -> void: + node.x = offset + var sibling := node.get_most_left_sibling() + var max_size: int = node.item.size.x + while sibling != null: + max_size = max(sibling.item.size.x, max_size) + sibling = sibling.get_next_sibling() + + for child in node.children: + _calculate_x(child, max_size + offset + LEVEL_DISTANCE * BeehaveUtils.get_editor_scale()) + + +func _calculate_y(node: TreeNode, offset: int) -> void: + node.y = offset + var sibling := node.get_most_left_sibling() + var max_size: int = node.item.size.y + while sibling != null: + max_size = max(sibling.item.size.y, max_size) + sibling = sibling.get_next_sibling() + + for child in node.children: + _calculate_y(child, max_size + offset + LEVEL_DISTANCE * BeehaveUtils.get_editor_scale()) diff --git a/addons/beehave/icons/action.svg b/addons/beehave/icons/action.svg new file mode 100644 index 0000000..3916c89 --- /dev/null +++ b/addons/beehave/icons/action.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/action.svg.import b/addons/beehave/icons/action.svg.import new file mode 100644 index 0000000..cf8a612 --- /dev/null +++ b/addons/beehave/icons/action.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://btrq8e0kyxthg" +path="res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/action.svg" +dest_files=["res://.godot/imported/action.svg-e8a91246d0ba9ba3cf84290d65648f06.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/blackboard.svg b/addons/beehave/icons/blackboard.svg new file mode 100644 index 0000000..e4948a5 --- /dev/null +++ b/addons/beehave/icons/blackboard.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/blackboard.svg.import b/addons/beehave/icons/blackboard.svg.import new file mode 100644 index 0000000..4650058 --- /dev/null +++ b/addons/beehave/icons/blackboard.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dw7rom0hiff6c" +path="res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/blackboard.svg" +dest_files=["res://.godot/imported/blackboard.svg-18d4dfd4f6de558de250b67251ff1e69.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_bt.svg b/addons/beehave/icons/category_bt.svg new file mode 100644 index 0000000..8be61ae --- /dev/null +++ b/addons/beehave/icons/category_bt.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_bt.svg.import b/addons/beehave/icons/category_bt.svg.import new file mode 100644 index 0000000..c11e4f2 --- /dev/null +++ b/addons/beehave/icons/category_bt.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qpdd6ue7x82h" +path="res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_bt.svg" +dest_files=["res://.godot/imported/category_bt.svg-8537bebd1c5f62dca3d7ee7f17efeed4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_composite.svg b/addons/beehave/icons/category_composite.svg new file mode 100644 index 0000000..aa8b866 --- /dev/null +++ b/addons/beehave/icons/category_composite.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_composite.svg.import b/addons/beehave/icons/category_composite.svg.import new file mode 100644 index 0000000..0496273 --- /dev/null +++ b/addons/beehave/icons/category_composite.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://863s568sneja" +path="res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_composite.svg" +dest_files=["res://.godot/imported/category_composite.svg-43f66e63a7ccfa5ac8ec6da0583b3246.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_decorator.svg b/addons/beehave/icons/category_decorator.svg new file mode 100644 index 0000000..165e3d6 --- /dev/null +++ b/addons/beehave/icons/category_decorator.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_decorator.svg.import b/addons/beehave/icons/category_decorator.svg.import new file mode 100644 index 0000000..492f32e --- /dev/null +++ b/addons/beehave/icons/category_decorator.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2ie8m4ddawlb" +path="res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_decorator.svg" +dest_files=["res://.godot/imported/category_decorator.svg-79d598d6456f32724156248e09d6eaf3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/category_leaf.svg b/addons/beehave/icons/category_leaf.svg new file mode 100644 index 0000000..1482fe6 --- /dev/null +++ b/addons/beehave/icons/category_leaf.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/category_leaf.svg.import b/addons/beehave/icons/category_leaf.svg.import new file mode 100644 index 0000000..4ef9604 --- /dev/null +++ b/addons/beehave/icons/category_leaf.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://eq0sp4g3s75r" +path="res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/category_leaf.svg" +dest_files=["res://.godot/imported/category_leaf.svg-c740ecab6cfae632574ca5e39e46fd2e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/condition.svg b/addons/beehave/icons/condition.svg new file mode 100644 index 0000000..37b2c7a --- /dev/null +++ b/addons/beehave/icons/condition.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/condition.svg.import b/addons/beehave/icons/condition.svg.import new file mode 100644 index 0000000..ef59099 --- /dev/null +++ b/addons/beehave/icons/condition.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ck4toqx0nggiu" +path="res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/condition.svg" +dest_files=["res://.godot/imported/condition.svg-57892684b10a64086f68c09c388b17e5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/cooldown.svg b/addons/beehave/icons/cooldown.svg new file mode 100644 index 0000000..fbdfd6a --- /dev/null +++ b/addons/beehave/icons/cooldown.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/cooldown.svg.import b/addons/beehave/icons/cooldown.svg.import new file mode 100644 index 0000000..1414358 --- /dev/null +++ b/addons/beehave/icons/cooldown.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ckao52pluvfs1" +path="res://.godot/imported/cooldown.svg-2fb8975b5974e35bedad825abb9faf66.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/cooldown.svg" +dest_files=["res://.godot/imported/cooldown.svg-2fb8975b5974e35bedad825abb9faf66.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/beehave/icons/delayer.svg b/addons/beehave/icons/delayer.svg new file mode 100644 index 0000000..21cb617 --- /dev/null +++ b/addons/beehave/icons/delayer.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/addons/beehave/icons/delayer.svg.import b/addons/beehave/icons/delayer.svg.import new file mode 100644 index 0000000..33f4b7a --- /dev/null +++ b/addons/beehave/icons/delayer.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://daears4ljyeng" +path="res://.godot/imported/delayer.svg-6f92c97f61b1eb8679428f438e6b08c7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/delayer.svg" +dest_files=["res://.godot/imported/delayer.svg-6f92c97f61b1eb8679428f438e6b08c7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/beehave/icons/failer.svg b/addons/beehave/icons/failer.svg new file mode 100644 index 0000000..968f7e1 --- /dev/null +++ b/addons/beehave/icons/failer.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/failer.svg.import b/addons/beehave/icons/failer.svg.import new file mode 100644 index 0000000..989b556 --- /dev/null +++ b/addons/beehave/icons/failer.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2fj7htaqvcud" +path="res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/failer.svg" +dest_files=["res://.godot/imported/failer.svg-9a62b840e1eacc0437e7a67b14a302e4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/inverter.svg b/addons/beehave/icons/inverter.svg new file mode 100644 index 0000000..d4e791e --- /dev/null +++ b/addons/beehave/icons/inverter.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/inverter.svg.import b/addons/beehave/icons/inverter.svg.import new file mode 100644 index 0000000..e9050a8 --- /dev/null +++ b/addons/beehave/icons/inverter.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cffmoc3og8hux" +path="res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/inverter.svg" +dest_files=["res://.godot/imported/inverter.svg-1f1b976d95de42c4ad99a92fa9a6c5d0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/limiter.svg b/addons/beehave/icons/limiter.svg new file mode 100644 index 0000000..7b3fa1d --- /dev/null +++ b/addons/beehave/icons/limiter.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/limiter.svg.import b/addons/beehave/icons/limiter.svg.import new file mode 100644 index 0000000..7b56b08 --- /dev/null +++ b/addons/beehave/icons/limiter.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c7akxvsg0f2by" +path="res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/limiter.svg" +dest_files=["res://.godot/imported/limiter.svg-b4c7646605c46f53c5e403fe21d8f584.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/repeater.svg b/addons/beehave/icons/repeater.svg new file mode 100644 index 0000000..47c46e9 --- /dev/null +++ b/addons/beehave/icons/repeater.svg @@ -0,0 +1,40 @@ + + + + + + + diff --git a/addons/beehave/icons/repeater.svg.import b/addons/beehave/icons/repeater.svg.import new file mode 100644 index 0000000..cd94266 --- /dev/null +++ b/addons/beehave/icons/repeater.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mkdpp16e57j0" +path="res://.godot/imported/repeater.svg-be2d3a7f1a46d7ba1d1939553725f598.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/repeater.svg" +dest_files=["res://.godot/imported/repeater.svg-be2d3a7f1a46d7ba1d1939553725f598.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/beehave/icons/selector.svg b/addons/beehave/icons/selector.svg new file mode 100644 index 0000000..0ae3b7a --- /dev/null +++ b/addons/beehave/icons/selector.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/selector.svg.import b/addons/beehave/icons/selector.svg.import new file mode 100644 index 0000000..ef7326d --- /dev/null +++ b/addons/beehave/icons/selector.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2c5d20doh4sp" +path="res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/selector.svg" +dest_files=["res://.godot/imported/selector.svg-78bccfc448bd1676b5a29bfde4b08e5b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/selector_random.svg b/addons/beehave/icons/selector_random.svg new file mode 100644 index 0000000..6f631e9 --- /dev/null +++ b/addons/beehave/icons/selector_random.svg @@ -0,0 +1,35 @@ + + diff --git a/addons/beehave/icons/selector_random.svg.import b/addons/beehave/icons/selector_random.svg.import new file mode 100644 index 0000000..6306f76 --- /dev/null +++ b/addons/beehave/icons/selector_random.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmnkcmk7bkdjd" +path="res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/selector_random.svg" +dest_files=["res://.godot/imported/selector_random.svg-d52fea1352c24483ecd9dc8609cf00f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/selector_reactive.svg b/addons/beehave/icons/selector_reactive.svg new file mode 100644 index 0000000..6db005f --- /dev/null +++ b/addons/beehave/icons/selector_reactive.svg @@ -0,0 +1,45 @@ + + diff --git a/addons/beehave/icons/selector_reactive.svg.import b/addons/beehave/icons/selector_reactive.svg.import new file mode 100644 index 0000000..12a8c5b --- /dev/null +++ b/addons/beehave/icons/selector_reactive.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://crkbov0h8sb8l" +path="res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/selector_reactive.svg" +dest_files=["res://.godot/imported/selector_reactive.svg-dd3b8fb8cd2ffe331605aaad1e021cc0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/sequence.svg b/addons/beehave/icons/sequence.svg new file mode 100644 index 0000000..3ebedd9 --- /dev/null +++ b/addons/beehave/icons/sequence.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/sequence.svg.import b/addons/beehave/icons/sequence.svg.import new file mode 100644 index 0000000..5dadbe2 --- /dev/null +++ b/addons/beehave/icons/sequence.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5gw354thiofm" +path="res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/sequence.svg" +dest_files=["res://.godot/imported/sequence.svg-76e5600611900cc81e9ec286977b8c6a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/sequence_random.svg b/addons/beehave/icons/sequence_random.svg new file mode 100644 index 0000000..34e4a12 --- /dev/null +++ b/addons/beehave/icons/sequence_random.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/sequence_random.svg.import b/addons/beehave/icons/sequence_random.svg.import new file mode 100644 index 0000000..3907462 --- /dev/null +++ b/addons/beehave/icons/sequence_random.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bat8ptdw5qt1d" +path="res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/sequence_random.svg" +dest_files=["res://.godot/imported/sequence_random.svg-58cee9098c622ef87db941279206422a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/sequence_reactive.svg b/addons/beehave/icons/sequence_reactive.svg new file mode 100644 index 0000000..33d219b --- /dev/null +++ b/addons/beehave/icons/sequence_reactive.svg @@ -0,0 +1,60 @@ + + diff --git a/addons/beehave/icons/sequence_reactive.svg.import b/addons/beehave/icons/sequence_reactive.svg.import new file mode 100644 index 0000000..ab0fa25 --- /dev/null +++ b/addons/beehave/icons/sequence_reactive.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rmiu1slwfkh7" +path="res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/sequence_reactive.svg" +dest_files=["res://.godot/imported/sequence_reactive.svg-7d384ca290f7934adb9e17d9e7116b6c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/simple_parallel.svg b/addons/beehave/icons/simple_parallel.svg new file mode 100644 index 0000000..e9c8b00 --- /dev/null +++ b/addons/beehave/icons/simple_parallel.svg @@ -0,0 +1,13 @@ + + simple_parallel + + Layer 1 + + + + + + + + + \ No newline at end of file diff --git a/addons/beehave/icons/simple_parallel.svg.import b/addons/beehave/icons/simple_parallel.svg.import new file mode 100644 index 0000000..b15ec65 --- /dev/null +++ b/addons/beehave/icons/simple_parallel.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://lgxtdlmsrnm4" +path="res://.godot/imported/simple_parallel.svg-3d4107eaf2e46557f6d3be3249f91430.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/simple_parallel.svg" +dest_files=["res://.godot/imported/simple_parallel.svg-3d4107eaf2e46557f6d3be3249f91430.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/beehave/icons/succeeder.svg b/addons/beehave/icons/succeeder.svg new file mode 100644 index 0000000..10f5912 --- /dev/null +++ b/addons/beehave/icons/succeeder.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/succeeder.svg.import b/addons/beehave/icons/succeeder.svg.import new file mode 100644 index 0000000..0cb7334 --- /dev/null +++ b/addons/beehave/icons/succeeder.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl6wo332kglbe" +path="res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/succeeder.svg" +dest_files=["res://.godot/imported/succeeder.svg-e5cf6f6e04b9b862b82fd2cb479272aa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/tree.svg b/addons/beehave/icons/tree.svg new file mode 100644 index 0000000..6c85ea1 --- /dev/null +++ b/addons/beehave/icons/tree.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/addons/beehave/icons/tree.svg.import b/addons/beehave/icons/tree.svg.import new file mode 100644 index 0000000..9ac0308 --- /dev/null +++ b/addons/beehave/icons/tree.svg.import @@ -0,0 +1,38 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://deryyg2hbmaaw" +path="res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex" +metadata={ +"has_editor_variant": true, +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/tree.svg" +dest_files=["res://.godot/imported/tree.svg-c0b20ed88b2fe300c0296f7236049076.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=true +editor/convert_colors_with_editor_theme=true diff --git a/addons/beehave/icons/until_fail.svg b/addons/beehave/icons/until_fail.svg new file mode 100644 index 0000000..c64a0a0 --- /dev/null +++ b/addons/beehave/icons/until_fail.svg @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/addons/beehave/icons/until_fail.svg.import b/addons/beehave/icons/until_fail.svg.import new file mode 100644 index 0000000..8a04fbf --- /dev/null +++ b/addons/beehave/icons/until_fail.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dbonu175jh64l" +path="res://.godot/imported/until_fail.svg-8015014c40e91d9c2668ec34d4118b8e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/beehave/icons/until_fail.svg" +dest_files=["res://.godot/imported/until_fail.svg-8015014c40e91d9c2668ec34d4118b8e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/beehave/metrics/beehave_global_metrics.gd b/addons/beehave/metrics/beehave_global_metrics.gd new file mode 100644 index 0000000..ef29db2 --- /dev/null +++ b/addons/beehave/metrics/beehave_global_metrics.gd @@ -0,0 +1,54 @@ +extends Node + +var _tree_count: int = 0 +var _active_tree_count: int = 0 +var _registered_trees: Array = [] + + +func _enter_tree() -> void: + Performance.add_custom_monitor("beehave/total_trees", _get_total_trees) + Performance.add_custom_monitor("beehave/total_enabled_trees", _get_total_enabled_trees) + + +func register_tree(tree) -> void: + if _registered_trees.has(tree): + return + + _registered_trees.append(tree) + _tree_count += 1 + + if tree.enabled: + _active_tree_count += 1 + + tree.tree_enabled.connect(_on_tree_enabled) + tree.tree_disabled.connect(_on_tree_disabled) + + +func unregister_tree(tree) -> void: + if not _registered_trees.has(tree): + return + + _registered_trees.erase(tree) + _tree_count -= 1 + + if tree.enabled: + _active_tree_count -= 1 + + tree.tree_enabled.disconnect(_on_tree_enabled) + tree.tree_disabled.disconnect(_on_tree_disabled) + + +func _get_total_trees() -> int: + return _tree_count + + +func _get_total_enabled_trees() -> int: + return _active_tree_count + + +func _on_tree_enabled() -> void: + _active_tree_count += 1 + + +func _on_tree_disabled() -> void: + _active_tree_count -= 1 diff --git a/addons/beehave/nodes/beehave_node.gd b/addons/beehave/nodes/beehave_node.gd new file mode 100644 index 0000000..9ab8d6e --- /dev/null +++ b/addons/beehave/nodes/beehave_node.gd @@ -0,0 +1,46 @@ +@tool +class_name BeehaveNode extends Node + +## A node in the behavior tree. Every node must return `SUCCESS`, `FAILURE` or +## `RUNNING` when ticked. + +enum { SUCCESS, FAILURE, RUNNING } + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = [] + + if get_children().any(func(x): return not (x is BeehaveNode)): + warnings.append("All children of this node should inherit from BeehaveNode class.") + + return warnings + + +## Executes this node and returns a status code. +## This method must be overwritten. +func tick(actor: Node, blackboard: Blackboard) -> int: + return SUCCESS + + +## Called when this node needs to be interrupted before it can return FAILURE or SUCCESS. +func interrupt(actor: Node, blackboard: Blackboard) -> void: + pass + + +## Called before the first time it ticks by the parent. +func before_run(actor: Node, blackboard: Blackboard) -> void: + pass + + +## Called after the last time it ticks and returns +## [code]SUCCESS[/code] or [code]FAILURE[/code]. +func after_run(actor: Node, blackboard: Blackboard) -> void: + pass + + +func get_class_name() -> Array[StringName]: + return [&"BeehaveNode"] + + +func can_send_message(blackboard: Blackboard) -> bool: + return blackboard.get_value("can_send_message", false) diff --git a/addons/beehave/nodes/beehave_tree.gd b/addons/beehave/nodes/beehave_tree.gd new file mode 100644 index 0000000..e27edbd --- /dev/null +++ b/addons/beehave/nodes/beehave_tree.gd @@ -0,0 +1,329 @@ +@tool +@icon("../icons/tree.svg") +class_name BeehaveTree extends Node + +## Controls the flow of execution of the entire behavior tree. + +enum { SUCCESS, FAILURE, RUNNING } + +enum ProcessThread { IDLE, PHYSICS } + +signal tree_enabled +signal tree_disabled + +## Whether this behavior tree should be enabled or not. +@export var enabled: bool = true: + set(value): + enabled = value + set_physics_process(enabled and process_thread == ProcessThread.PHYSICS) + set_process(enabled and process_thread == ProcessThread.IDLE) + if value: + tree_enabled.emit() + else: + interrupt() + tree_disabled.emit() + + get: + return enabled + +## How often the tree should tick, in frames. The default value of 1 means +## tick() runs every frame. +@export var tick_rate: int = 1 + +## An optional node path this behavior tree should apply to. +@export_node_path var actor_node_path: NodePath: + set(anp): + actor_node_path = anp + if actor_node_path != null and str(actor_node_path) != "..": + actor = get_node(actor_node_path) + else: + actor = get_parent() + if Engine.is_editor_hint(): + update_configuration_warnings() + +## Whether to run this tree in a physics or idle thread. +@export var process_thread: ProcessThread = ProcessThread.PHYSICS: + set(value): + process_thread = value + set_physics_process(enabled and process_thread == ProcessThread.PHYSICS) + set_process(enabled and process_thread == ProcessThread.IDLE) + +## Custom blackboard node. An internal blackboard will be used +## if no blackboard is provided explicitly. +@export var blackboard: Blackboard: + set(b): + blackboard = b + if blackboard and _internal_blackboard: + remove_child(_internal_blackboard) + _internal_blackboard.free() + _internal_blackboard = null + elif not blackboard and not _internal_blackboard: + _internal_blackboard = Blackboard.new() + add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) + get: + # in case blackboard is accessed before this node is, + # we need to ensure that the internal blackboard is used. + if not blackboard and not _internal_blackboard: + _internal_blackboard = Blackboard.new() + add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) + return blackboard if blackboard else _internal_blackboard + +## When enabled, this tree is tracked individually +## as a custom monitor. +@export var custom_monitor = false: + set(b): + custom_monitor = b + if custom_monitor and _process_time_metric_name != "": + Performance.add_custom_monitor( + _process_time_metric_name, _get_process_time_metric_value + ) + _get_global_metrics().register_tree(self) + else: + if _process_time_metric_name != "": + # Remove tree metric from the engine + Performance.remove_custom_monitor(_process_time_metric_name) + _get_global_metrics().unregister_tree(self) + + BeehaveDebuggerMessages.unregister_tree(get_instance_id()) + +@export var actor: Node: + set(a): + actor = a + if actor == null: + actor = get_parent() + if Engine.is_editor_hint(): + update_configuration_warnings() + +var status: int = -1 +var last_tick: int = 0 + +var _internal_blackboard: Blackboard +var _process_time_metric_name: String +var _process_time_metric_value: float = 0.0 +var _can_send_message: bool = false + + +func _ready() -> void: + var connect_scene_tree_signal = func(signal_name: String, is_added: bool): + if not get_tree().is_connected(signal_name, _on_scene_tree_node_added_removed.bind(is_added)): + get_tree().connect(signal_name, _on_scene_tree_node_added_removed.bind(is_added)) + connect_scene_tree_signal.call("node_added", true) + connect_scene_tree_signal.call("node_removed", false) + + if not process_thread: + process_thread = ProcessThread.PHYSICS + + if not actor: + if actor_node_path: + actor = get_node(actor_node_path) + else: + actor = get_parent() + + if not blackboard: + # invoke setter to auto-initialise the blackboard. + self.blackboard = null + + # Get the name of the parent node name for metric + _process_time_metric_name = ( + "beehave [microseconds]/process_time_%s-%s" % [actor.name, get_instance_id()] + ) + + set_physics_process(enabled and process_thread == ProcessThread.PHYSICS) + set_process(enabled and process_thread == ProcessThread.IDLE) + + # Register custom metric to the engine + if custom_monitor and not Engine.is_editor_hint(): + Performance.add_custom_monitor(_process_time_metric_name, _get_process_time_metric_value) + _get_global_metrics().register_tree(self) + + if Engine.is_editor_hint(): + update_configuration_warnings.call_deferred() + else: + _get_global_debugger().register_tree(self) + BeehaveDebuggerMessages.register_tree(_get_debugger_data(self)) + + # Randomize at what frames tick() will happen to avoid stutters + last_tick = randi_range(0, tick_rate - 1) + + +func _on_scene_tree_node_added_removed(node: Node, is_added: bool) -> void: + if Engine.is_editor_hint(): + return + + if node is BeehaveNode and is_ancestor_of(node): + var sgnal := node.ready if is_added else node.tree_exited + if is_added: + sgnal.connect( + func() -> void: BeehaveDebuggerMessages.register_tree(_get_debugger_data(self)), + CONNECT_ONE_SHOT + ) + else: + sgnal.connect( + func() -> void: + BeehaveDebuggerMessages.unregister_tree(get_instance_id()) + request_ready() + ) + + +func _physics_process(_delta: float) -> void: + _process_internally() + + +func _process(_delta: float) -> void: + _process_internally() + + +func _process_internally() -> void: + if Engine.is_editor_hint(): + return + + if last_tick < tick_rate - 1: + last_tick += 1 + return + + last_tick = 0 + + # Start timing for metric + var start_time = Time.get_ticks_usec() + + blackboard.set_value("can_send_message", _can_send_message) + + if _can_send_message: + BeehaveDebuggerMessages.process_begin(get_instance_id()) + + if self.get_child_count() == 1: + tick() + + if _can_send_message: + BeehaveDebuggerMessages.process_end(get_instance_id()) + + # Check the cost for this frame and save it for metric report + _process_time_metric_value = Time.get_ticks_usec() - start_time + + +func tick() -> int: + if actor == null or get_child_count() == 0: + return FAILURE + var child := self.get_child(0) + if status != RUNNING: + child.before_run(actor, blackboard) + + status = child.tick(actor, blackboard) + if _can_send_message: + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), status) + BeehaveDebuggerMessages.process_tick(get_instance_id(), status) + + # Clear running action if nothing is running + if status != RUNNING: + blackboard.set_value("running_action", null, str(actor.get_instance_id())) + child.after_run(actor, blackboard) + + return status + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = [] + + if actor == null: + warnings.append("Configure target node on tree") + + if get_children().any(func(x): return not (x is BeehaveNode)): + warnings.append("All children of this node should inherit from BeehaveNode class.") + + if get_child_count() != 1: + warnings.append("BeehaveTree should have exactly one child node.") + + return warnings + + +## Returns the currently running action +func get_running_action() -> ActionLeaf: + return blackboard.get_value("running_action", null, str(actor.get_instance_id())) + + +## Returns the last condition that was executed +func get_last_condition() -> ConditionLeaf: + return blackboard.get_value("last_condition", null, str(actor.get_instance_id())) + + +## Returns the status of the last executed condition +func get_last_condition_status() -> String: + if blackboard.has_value("last_condition_status", str(actor.get_instance_id())): + var status = blackboard.get_value( + "last_condition_status", null, str(actor.get_instance_id()) + ) + if status == SUCCESS: + return "SUCCESS" + elif status == FAILURE: + return "FAILURE" + else: + return "RUNNING" + return "" + + +## interrupts this tree if anything was running +func interrupt() -> void: + if self.get_child_count() != 0: + var first_child = self.get_child(0) + if "interrupt" in first_child: + first_child.interrupt(actor, blackboard) + + +## Enables this tree. +func enable() -> void: + self.enabled = true + + +## Disables this tree. +func disable() -> void: + self.enabled = false + + +func _exit_tree() -> void: + if custom_monitor: + if _process_time_metric_name != "": + # Remove tree metric from the engine + Performance.remove_custom_monitor(_process_time_metric_name) + _get_global_metrics().unregister_tree(self) + + BeehaveDebuggerMessages.unregister_tree(get_instance_id()) + + +# Called by the engine to profile this tree +func _get_process_time_metric_value() -> int: + return int(_process_time_metric_value) + + +func _get_debugger_data(node: Node) -> Dictionary: + if not (node is BeehaveTree or node is BeehaveNode): + return {} + + var data := { + path = node.get_path(), + name = node.name, + type = node.get_class_name(), + id = str(node.get_instance_id()) + } + if node.get_child_count() > 0: + data.children = [] + for child in node.get_children(): + var child_data := _get_debugger_data(child) + if not child_data.is_empty(): + data.children.push_back(child_data) + return data + + +func get_class_name() -> Array[StringName]: + return [&"BeehaveTree"] + + +# required to avoid lifecycle issues on initial load +# due to loading order problems with autoloads +func _get_global_metrics() -> Node: + return get_tree().root.get_node("BeehaveGlobalMetrics") + + +# required to avoid lifecycle issues on initial load +# due to loading order problems with autoloads +func _get_global_debugger() -> Node: + return get_tree().root.get_node("BeehaveGlobalDebugger") diff --git a/addons/beehave/nodes/composites/composite.gd b/addons/beehave/nodes/composites/composite.gd new file mode 100644 index 0000000..53a8e90 --- /dev/null +++ b/addons/beehave/nodes/composites/composite.gd @@ -0,0 +1,34 @@ +@tool +@icon("../../icons/category_composite.svg") +class_name Composite extends BeehaveNode + +## A Composite node controls the flow of execution of its children in a specific manner. + +var running_child: BeehaveNode = null + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = super._get_configuration_warnings() + + if get_children().filter(func(x): return x is BeehaveNode).size() < 2: + warnings.append( + "Any composite node should have at least two children. Otherwise it is not useful." + ) + + return warnings + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + if running_child != null: + running_child.interrupt(actor, blackboard) + running_child = null + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + running_child = null + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"Composite") + return classes diff --git a/addons/beehave/nodes/composites/randomized_composite.gd b/addons/beehave/nodes/composites/randomized_composite.gd new file mode 100644 index 0000000..2afa5e9 --- /dev/null +++ b/addons/beehave/nodes/composites/randomized_composite.gd @@ -0,0 +1,176 @@ +@tool +class_name RandomizedComposite extends Composite + +const WEIGHTS_PREFIX = "Weights/" + +## Sets a predicable seed +@export var random_seed: int = 0: + set(rs): + random_seed = rs + if random_seed != 0: + seed(random_seed) + else: + randomize() + +## Wether to use weights for every child or not. +@export var use_weights: bool: + set(value): + use_weights = value + if use_weights: + _update_weights(get_children()) + _connect_children_changing_signals() + notify_property_list_changed() + +var _weights: Dictionary +var _exiting_tree: bool + + +func _ready(): + _connect_children_changing_signals() + + +func _connect_children_changing_signals(): + if not child_entered_tree.is_connected(_on_child_entered_tree): + child_entered_tree.connect(_on_child_entered_tree) + + if not child_exiting_tree.is_connected(_on_child_exiting_tree): + child_exiting_tree.connect(_on_child_exiting_tree) + + +func get_shuffled_children() -> Array[Node]: + var children_bag: Array[Node] = get_children().duplicate() + if use_weights: + var weights: Array[int] + weights.assign(children_bag.map(func(child): return _weights[child.name])) + children_bag.assign(_weighted_shuffle(children_bag, weights)) + else: + children_bag.shuffle() + return children_bag + + +## Returns a shuffled version of a given array using the supplied array of weights. +## Think of weights as the chance of a given item being the first in the array. +func _weighted_shuffle(items: Array, weights: Array[int]) -> Array: + if len(items) != len(weights): + push_error( + ( + "items and weights size mismatch: expected %d weights, got %d instead." + % [len(items), len(weights)] + ) + ) + return items + + # This method is based on the weighted random sampling algorithm + # by Efraimidis, Spirakis; 2005. This runs in O(n log(n)). + + # For each index, it will calculate random_value^(1/weight). + var chance_calc = func(i): return [i, randf() ** (1.0 / weights[i])] + var random_distribuition = range(len(items)).map(chance_calc) + + # Now we just have to order by the calculated value, descending. + random_distribuition.sort_custom(func(a, b): return a[1] > b[1]) + + return random_distribuition.map(func(dist): return items[dist[0]]) + + +func _get_property_list(): + var properties = [] + + if use_weights: + for key in _weights.keys(): + properties.append( + { + "name": WEIGHTS_PREFIX + key, + "type": TYPE_INT, + "usage": PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "1,100" + } + ) + + return properties + + +func _set(property: StringName, value: Variant) -> bool: + if property.begins_with(WEIGHTS_PREFIX): + var weight_name = property.trim_prefix(WEIGHTS_PREFIX) + _weights[weight_name] = value + return true + + return false + + +func _get(property: StringName): + if property.begins_with(WEIGHTS_PREFIX): + var weight_name = property.trim_prefix(WEIGHTS_PREFIX) + return _weights[weight_name] + + return null + + +func _update_weights(children: Array[Node]) -> void: + var new_weights = {} + for c in children: + if _weights.has(c.name): + new_weights[c.name] = _weights[c.name] + else: + new_weights[c.name] = 1 + _weights = new_weights + notify_property_list_changed() + + +func _exit_tree() -> void: + _exiting_tree = true + + +func _enter_tree() -> void: + _exiting_tree = false + + +func _on_child_entered_tree(node: Node): + _update_weights(get_children()) + + var renamed_callable = _on_child_renamed.bind(node.name, node) + if not node.renamed.is_connected(renamed_callable): + node.renamed.connect(renamed_callable) + + if not node.tree_exited.is_connected(_on_child_tree_exited): + node.tree_exited.connect(_on_child_tree_exited.bind(node)) + + +func _on_child_exiting_tree(node: Node): + var renamed_callable = _on_child_renamed.bind(node.name, node) + if node.renamed.is_connected(renamed_callable): + node.renamed.disconnect(renamed_callable) + + +func _on_child_tree_exited(node: Node) -> void: + # don't erase the individual child if the whole tree is exiting together + if not _exiting_tree: + var children = get_children() + children.erase(node) + _update_weights(children) + + if node.tree_exited.is_connected(_on_child_tree_exited): + node.tree_exited.disconnect(_on_child_tree_exited) + + +func _on_child_renamed(old_name: String, renamed_child: Node): + if old_name == renamed_child.name: + return # No need to update the weights. + + # Disconnect signal with old name... + renamed_child.renamed.disconnect(_on_child_renamed.bind(old_name, renamed_child)) + # ...and connect with the new name. + renamed_child.renamed.connect(_on_child_renamed.bind(renamed_child.name, renamed_child)) + + var original_weight = _weights[old_name] + _weights.erase(old_name) + _weights[renamed_child.name] = original_weight + notify_property_list_changed() + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"RandomizedComposite") + return classes diff --git a/addons/beehave/nodes/composites/selector.gd b/addons/beehave/nodes/composites/selector.gd new file mode 100644 index 0000000..618f602 --- /dev/null +++ b/addons/beehave/nodes/composites/selector.gd @@ -0,0 +1,69 @@ +@tool +@icon("../../icons/selector.svg") +class_name SelectorComposite extends Composite + +## Selector nodes will attempt to execute each of its children until one of +## them return `SUCCESS`. If all children return `FAILURE`, this node will also +## return `FAILURE`. +## If a child returns `RUNNING` it will tick again. + +var last_execution_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < last_execution_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _cleanup_running_task(c, actor, blackboard) + c.after_run(actor, blackboard) + return SUCCESS + FAILURE: + _cleanup_running_task(c, actor, blackboard) + last_execution_index += 1 + c.after_run(actor, blackboard) + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return FAILURE + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + last_execution_index = 0 + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + last_execution_index = 0 + super(actor, blackboard) + + +## Changes `running_action` and `running_child` after the node finishes executing. +func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): + var blackboard_name = str(actor.get_instance_id()) + if finished_action == running_child: + running_child = null + if finished_action == blackboard.get_value("running_action", null, blackboard_name): + blackboard.set_value("running_action", null, blackboard_name) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SelectorComposite") + return classes diff --git a/addons/beehave/nodes/composites/selector_random.gd b/addons/beehave/nodes/composites/selector_random.gd new file mode 100644 index 0000000..7a42300 --- /dev/null +++ b/addons/beehave/nodes/composites/selector_random.gd @@ -0,0 +1,82 @@ +@tool +@icon("../../icons/selector_random.svg") +class_name SelectorRandomComposite extends RandomizedComposite + +## This node will attempt to execute all of its children just like a +## [code]SelectorStar[/code] would, with the exception that the children +## will be executed in a random order. + +## A shuffled list of the children that will be executed in reverse order. +var _children_bag: Array[Node] = [] +var c: Node + + +func _ready() -> void: + super() + if random_seed == 0: + randomize() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if _children_bag.is_empty(): + _reset() + + # We need to traverse the array in reverse since we will be manipulating it. + for i in _get_reversed_indexes(): + c = _children_bag[i] + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _children_bag.erase(c) + c.after_run(actor, blackboard) + return SUCCESS + FAILURE: + _children_bag.erase(c) + c.after_run(actor, blackboard) + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return FAILURE + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _get_reversed_indexes() -> Array[int]: + var reversed: Array[int] + reversed.assign(range(_children_bag.size())) + reversed.reverse() + return reversed + + +func _reset() -> void: + var new_order = get_shuffled_children() + _children_bag = new_order.duplicate() + _children_bag.reverse() # It needs to run the children in reverse order. + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SelectorRandomComposite") + return classes diff --git a/addons/beehave/nodes/composites/selector_reactive.gd b/addons/beehave/nodes/composites/selector_reactive.gd new file mode 100644 index 0000000..fa164d0 --- /dev/null +++ b/addons/beehave/nodes/composites/selector_reactive.gd @@ -0,0 +1,47 @@ +@tool +@icon("../../icons/selector_reactive.svg") +class_name SelectorReactiveComposite extends Composite + +## Selector Reactive nodes will attempt to execute each of its children until one of +## them return `SUCCESS`. If all children return `FAILURE`, this node will also +## return `FAILURE`. +## If a child returns `RUNNING` it will restart. + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + # Interrupt any child that was RUNNING before. + if c != running_child: + interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return SUCCESS + FAILURE: + c.after_run(actor, blackboard) + RUNNING: + if c != running_child: + interrupt(actor, blackboard) + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return FAILURE + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SelectorReactiveComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence.gd b/addons/beehave/nodes/composites/sequence.gd new file mode 100644 index 0000000..d308e8d --- /dev/null +++ b/addons/beehave/nodes/composites/sequence.gd @@ -0,0 +1,75 @@ +@tool +@icon("../../icons/sequence.svg") +class_name SequenceComposite extends Composite + +## Sequence nodes will attempt to execute all of its children and report +## `SUCCESS` in case all of the children report a `SUCCESS` status code. +## If at least one child reports a `FAILURE` status code, this node will also +## return `FAILURE` and restart. +## In case a child returns `RUNNING` this node will tick again. + +var successful_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < successful_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _cleanup_running_task(c, actor, blackboard) + successful_index += 1 + c.after_run(actor, blackboard) + FAILURE: + _cleanup_running_task(c, actor, blackboard) + # Interrupt any child that was RUNNING before. + interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + if c != running_child: + if running_child != null: + running_child.interrupt(actor, blackboard) + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + _reset() + return SUCCESS + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _reset() -> void: + successful_index = 0 + + +## Changes `running_action` and `running_child` after the node finishes executing. +func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): + var blackboard_name = str(actor.get_instance_id()) + if finished_action == running_child: + running_child = null + if finished_action == blackboard.get_value("running_action", null, blackboard_name): + blackboard.set_value("running_action", null, blackboard_name) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence_random.gd b/addons/beehave/nodes/composites/sequence_random.gd new file mode 100644 index 0000000..a744f52 --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_random.gd @@ -0,0 +1,96 @@ +@tool +@icon("../../icons/sequence_random.svg") +class_name SequenceRandomComposite extends RandomizedComposite + +## This node will attempt to execute all of its children just like a +## [code]SequenceStar[/code] would, with the exception that the children +## will be executed in a random order. + +# Emitted whenever the children are shuffled. +signal reset(new_order: Array[Node]) + +## Whether the sequence should start where it left off after a previous failure. +@export var resume_on_failure: bool = false +## Whether the sequence should start where it left off after a previous interruption. +@export var resume_on_interrupt: bool = false + +## A shuffled list of the children that will be executed in reverse order. +var _children_bag: Array[Node] = [] +var c: Node + + +func _ready() -> void: + super() + if random_seed == 0: + randomize() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if _children_bag.is_empty(): + _reset() + + # We need to traverse the array in reverse since we will be manipulating it. + for i in _get_reversed_indexes(): + c = _children_bag[i] + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + _children_bag.erase(c) + c.after_run(actor, blackboard) + FAILURE: + _children_bag.erase(c) + # Interrupt any child that was RUNNING before + # but do not reset! + super.interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + + return SUCCESS + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + if not resume_on_failure: + _reset() + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + if not resume_on_interrupt: + _reset() + super(actor, blackboard) + + +func _get_reversed_indexes() -> Array[int]: + var reversed: Array[int] + reversed.assign(range(_children_bag.size())) + reversed.reverse() + return reversed + + +func _reset() -> void: + var new_order = get_shuffled_children() + _children_bag = new_order.duplicate() + _children_bag.reverse() # It needs to run the children in reverse order. + reset.emit(new_order) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceRandomComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence_reactive.gd b/addons/beehave/nodes/composites/sequence_reactive.gd new file mode 100644 index 0000000..f40d8ab --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_reactive.gd @@ -0,0 +1,63 @@ +@tool +@icon("../../icons/sequence_reactive.svg") +class_name SequenceReactiveComposite extends Composite + +## Reactive Sequence nodes will attempt to execute all of its children and report +## `SUCCESS` in case all of the children report a `SUCCESS` status code. +## If at least one child reports a `FAILURE` status code, this node will also +## return `FAILURE` and restart. +## In case a child returns `RUNNING` this node will restart. + +var successful_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < successful_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + successful_index += 1 + c.after_run(actor, blackboard) + FAILURE: + # Interrupt any child that was RUNNING before. + interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + _reset() + if running_child != c: + interrupt(actor, blackboard) + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + _reset() + return SUCCESS + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _reset() -> void: + successful_index = 0 + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceReactiveComposite") + return classes diff --git a/addons/beehave/nodes/composites/sequence_star.gd b/addons/beehave/nodes/composites/sequence_star.gd new file mode 100644 index 0000000..36bb19c --- /dev/null +++ b/addons/beehave/nodes/composites/sequence_star.gd @@ -0,0 +1,61 @@ +@tool +@icon("../../icons/sequence_reactive.svg") +class_name SequenceStarComposite extends Composite + +## Sequence Star nodes will attempt to execute all of its children and report +## `SUCCESS` in case all of the children report a `SUCCESS` status code. +## If at least one child reports a `FAILURE` status code, this node will also +## return `FAILURE` and tick again. +## In case a child returns `RUNNING` this node will tick again. + +var successful_index: int = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + for c in get_children(): + if c.get_index() < successful_index: + continue + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + successful_index += 1 + c.after_run(actor, blackboard) + FAILURE: + # Interrupt any child that was RUNNING before + # but do not reset! + super.interrupt(actor, blackboard) + c.after_run(actor, blackboard) + return FAILURE + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + _reset() + return SUCCESS + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _reset() -> void: + successful_index = 0 + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SequenceStarComposite") + return classes diff --git a/addons/beehave/nodes/composites/simple_parallel.gd b/addons/beehave/nodes/composites/simple_parallel.gd new file mode 100644 index 0000000..4133d60 --- /dev/null +++ b/addons/beehave/nodes/composites/simple_parallel.gd @@ -0,0 +1,122 @@ +@tool +@icon("../../icons/simple_parallel.svg") +class_name SimpleParallelComposite extends Composite + +## Simple Parallel nodes will attampt to execute all chidren at same time and +## can only have exactly two children. First child as primary node, second +## child as secondary node. +## This node will always report primary node's state, and continue tick while +## primary node return 'RUNNING'. The state of secondary node will be ignored +## and executed like a subtree. +## If primary node return 'SUCCESS' or 'FAILURE', this node will interrupt +## secondary node and return primary node's result. +## If this node is running under delay mode, it will wait seconday node +## finish its action after primary node terminates. + +#how many times should secondary node repeat, zero means loop forever +@export var secondary_node_repeat_count: int = 0 + +#wether to wait secondary node finish its current action after primary node finished +@export var delay_mode: bool = false + +var delayed_result := SUCCESS +var main_task_finished: bool = false +var secondary_node_running: bool = false +var secondary_node_repeat_left: int = 0 + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = super._get_configuration_warnings() + + if get_child_count() != 2: + warnings.append("SimpleParallel should have exactly two child nodes.") + + if not get_child(0) is ActionLeaf: + warnings.append("SimpleParallel should have an action leaf node as first child node.") + + return warnings + + +func tick(actor, blackboard: Blackboard): + for c in get_children(): + var node_index = c.get_index() + if node_index == 0 and not main_task_finished: + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + delayed_result = response + match response: + SUCCESS, FAILURE: + _cleanup_running_task(c, actor, blackboard) + c.after_run(actor, blackboard) + main_task_finished = true + if not delay_mode: + if secondary_node_running: + get_child(1).interrupt(actor, blackboard) + _reset() + return delayed_result + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + + elif node_index == 1: + if secondary_node_repeat_count == 0 or secondary_node_repeat_left > 0: + if not secondary_node_running: + c.before_run(actor, blackboard) + var subtree_response = c.tick(actor, blackboard) + if subtree_response != RUNNING: + secondary_node_running = false + c.after_run(actor, blackboard) + if delay_mode and main_task_finished: + _reset() + return delayed_result + elif secondary_node_repeat_left > 0: + secondary_node_repeat_left -= 1 + else: + secondary_node_running = true + + return RUNNING + + +func before_run(actor: Node, blackboard: Blackboard) -> void: + secondary_node_repeat_left = secondary_node_repeat_count + super(actor, blackboard) + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + if not main_task_finished: + get_child(0).interrupt(actor, blackboard) + if secondary_node_running: + get_child(1).interrupt(actor, blackboard) + _reset() + super(actor, blackboard) + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + _reset() + super(actor, blackboard) + + +func _reset() -> void: + main_task_finished = false + secondary_node_running = false + + +## Changes `running_action` and `running_child` after the node finishes executing. +func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard): + var blackboard_name = str(actor.get_instance_id()) + if finished_action == running_child: + running_child = null + if finished_action == blackboard.get_value("running_action", null, blackboard_name): + blackboard.set_value("running_action", null, blackboard_name) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"SimpleParallelComposite") + return classes diff --git a/addons/beehave/nodes/decorators/cooldown.gd b/addons/beehave/nodes/decorators/cooldown.gd new file mode 100644 index 0000000..84c27b7 --- /dev/null +++ b/addons/beehave/nodes/decorators/cooldown.gd @@ -0,0 +1,49 @@ +@tool +@icon("../../icons/cooldown.svg") +extends Decorator +class_name CooldownDecorator + +## The Cooldown Decorator will return 'FAILURE' for a set amount of time +## after executing its child. +## The timer resets the next time its child is executed and it is not `RUNNING` + +## The wait time in seconds +@export var wait_time := 0.0 + +@onready var cache_key = "cooldown_%s" % self.get_instance_id() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + var remaining_time = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) + var response + + if c != running_child: + c.before_run(actor, blackboard) + + if remaining_time > 0: + response = FAILURE + + remaining_time -= get_physics_process_delta_time() + blackboard.set_value(cache_key, remaining_time, str(actor.get_instance_id())) + + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(self.get_instance_id(), response) + else: + response = c.tick(actor, blackboard) + + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING and c is ActionLeaf: + running_child = c + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + + if response != RUNNING: + blackboard.set_value(cache_key, wait_time, str(actor.get_instance_id())) + + return response diff --git a/addons/beehave/nodes/decorators/decorator.gd b/addons/beehave/nodes/decorators/decorator.gd new file mode 100644 index 0000000..3b93498 --- /dev/null +++ b/addons/beehave/nodes/decorators/decorator.gd @@ -0,0 +1,33 @@ +@tool +@icon("../../icons/category_decorator.svg") +class_name Decorator extends BeehaveNode + +## Decorator nodes are used to transform the result received by its child. +## Must only have one child. + +var running_child: BeehaveNode = null + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = super._get_configuration_warnings() + + if get_child_count() != 1: + warnings.append("Decorator should have exactly one child node.") + + return warnings + + +func interrupt(actor: Node, blackboard: Blackboard) -> void: + if running_child != null: + running_child.interrupt(actor, blackboard) + running_child = null + + +func after_run(actor: Node, blackboard: Blackboard) -> void: + running_child = null + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"Decorator") + return classes diff --git a/addons/beehave/nodes/decorators/delayer.gd b/addons/beehave/nodes/decorators/delayer.gd new file mode 100644 index 0000000..2b8b73c --- /dev/null +++ b/addons/beehave/nodes/decorators/delayer.gd @@ -0,0 +1,49 @@ +@tool +@icon("../../icons/delayer.svg") +extends Decorator +class_name DelayDecorator + +## The Delay Decorator will return 'RUNNING' for a set amount of time +## before executing its child. +## The timer resets when both it and its child are not `RUNNING` + +## The wait time in seconds +@export var wait_time := 0.0 + +@onready var cache_key = "time_limiter_%s" % self.get_instance_id() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + var total_time = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) + var response + + if c != running_child: + c.before_run(actor, blackboard) + + if total_time < wait_time: + response = RUNNING + + total_time += get_physics_process_delta_time() + blackboard.set_value(cache_key, total_time, str(actor.get_instance_id())) + + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(self.get_instance_id(), response) + else: + response = c.tick(actor, blackboard) + + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING and c is ActionLeaf: + running_child = c + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + + if response != RUNNING: + blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id())) + + return response diff --git a/addons/beehave/nodes/decorators/failer.gd b/addons/beehave/nodes/decorators/failer.gd new file mode 100644 index 0000000..e47312f --- /dev/null +++ b/addons/beehave/nodes/decorators/failer.gd @@ -0,0 +1,35 @@ +@tool +@icon("../../icons/failer.svg") +class_name AlwaysFailDecorator extends Decorator + +## A Failer node will always return a `FAILURE` status code. + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + else: + c.after_run(actor, blackboard) + return FAILURE + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"AlwaysFailDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/inverter.gd b/addons/beehave/nodes/decorators/inverter.gd new file mode 100644 index 0000000..2570677 --- /dev/null +++ b/addons/beehave/nodes/decorators/inverter.gd @@ -0,0 +1,43 @@ +@tool +@icon("../../icons/inverter.svg") +class_name InverterDecorator extends Decorator + +## An inverter will return `FAILURE` in case it's child returns a `SUCCESS` status +## code or `SUCCESS` in case its child returns a `FAILURE` status code. + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + match response: + SUCCESS: + c.after_run(actor, blackboard) + return FAILURE + FAILURE: + c.after_run(actor, blackboard) + return SUCCESS + RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + _: + push_error("This should be unreachable") + return -1 + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"InverterDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/limiter.gd b/addons/beehave/nodes/decorators/limiter.gd new file mode 100644 index 0000000..a38d7f3 --- /dev/null +++ b/addons/beehave/nodes/decorators/limiter.gd @@ -0,0 +1,60 @@ +@tool +@icon("../../icons/limiter.svg") +class_name LimiterDecorator extends Decorator + +## The limiter will execute its `RUNNING` child `x` amount of times. When the number of +## maximum ticks is reached, it will return a `FAILURE` status code. +## The count resets the next time that a child is not `RUNNING` + +@onready var cache_key = "limiter_%s" % self.get_instance_id() + +@export var max_count: float = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if not get_child_count() == 1: + return FAILURE + + var child = get_child(0) + var current_count = blackboard.get_value(cache_key, 0, str(actor.get_instance_id())) + + if current_count < max_count: + blackboard.set_value(cache_key, current_count + 1, str(actor.get_instance_id())) + var response = child.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) + + if child is ConditionLeaf: + blackboard.set_value("last_condition", child, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if child is ActionLeaf and response == RUNNING: + running_child = child + blackboard.set_value("running_action", child, str(actor.get_instance_id())) + + if response != RUNNING: + child.after_run(actor, blackboard) + + return response + else: + interrupt(actor, blackboard) + child.after_run(actor, blackboard) + return FAILURE + + +func before_run(actor: Node, blackboard: Blackboard) -> void: + blackboard.set_value(cache_key, 0, str(actor.get_instance_id())) + if get_child_count() > 0: + get_child(0).before_run(actor, blackboard) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"LimiterDecorator") + return classes + + +func _get_configuration_warnings() -> PackedStringArray: + if not get_child_count() == 1: + return ["Requires exactly one child node"] + return [] diff --git a/addons/beehave/nodes/decorators/repeater.gd b/addons/beehave/nodes/decorators/repeater.gd new file mode 100644 index 0000000..647f7e0 --- /dev/null +++ b/addons/beehave/nodes/decorators/repeater.gd @@ -0,0 +1,58 @@ +## The repeater will execute its child until it returns `SUCCESS` a certain amount of times. +## When the number of maximum ticks is reached, it will return a `SUCCESS` status code. +## If the child returns `FAILURE`, the repeater will return `FAILURE` immediately. +@tool +@icon("../../icons/repeater.svg") +class_name RepeaterDecorator extends Decorator + +@export var repetitions: int = 1 +var current_count: int = 0 + + +func before_run(actor: Node, blackboard: Blackboard): + current_count = 0 + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var child = get_child(0) + + if current_count < repetitions: + if running_child == null: + child.before_run(actor, blackboard) + + var response = child.tick(actor, blackboard) + + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) + + if child is ConditionLeaf: + blackboard.set_value("last_condition", child, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = child + if child is ActionLeaf: + blackboard.set_value("running_action", child, str(actor.get_instance_id())) + return RUNNING + + current_count += 1 + child.after_run(actor, blackboard) + + if running_child != null: + running_child = null + + if response == FAILURE: + return FAILURE + + if current_count >= repetitions: + return SUCCESS + + return RUNNING + else: + return SUCCESS + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"LimiterDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/succeeder.gd b/addons/beehave/nodes/decorators/succeeder.gd new file mode 100644 index 0000000..344b90f --- /dev/null +++ b/addons/beehave/nodes/decorators/succeeder.gd @@ -0,0 +1,35 @@ +@tool +@icon("../../icons/succeeder.svg") +class_name AlwaysSucceedDecorator extends Decorator + +## A succeeder node will always return a `SUCCESS` status code. + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + else: + c.after_run(actor, blackboard) + return SUCCESS + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"AlwaysSucceedDecorator") + return classes diff --git a/addons/beehave/nodes/decorators/time_limiter.gd b/addons/beehave/nodes/decorators/time_limiter.gd new file mode 100644 index 0000000..d9638d7 --- /dev/null +++ b/addons/beehave/nodes/decorators/time_limiter.gd @@ -0,0 +1,60 @@ +@tool +@icon("../../icons/limiter.svg") +class_name TimeLimiterDecorator extends Decorator + +## The Time Limit Decorator will give its `RUNNING` child a set amount of time to finish +## before interrupting it and return a `FAILURE` status code. +## The timer resets the next time that a child is not `RUNNING` + +@export var wait_time := 0.0 + +@onready var cache_key = "time_limiter_%s" % self.get_instance_id() + + +func tick(actor: Node, blackboard: Blackboard) -> int: + if not get_child_count() == 1: + return FAILURE + + var child = self.get_child(0) + var time_left = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id())) + + if time_left < wait_time: + time_left += get_physics_process_delta_time() + blackboard.set_value(cache_key, time_left, str(actor.get_instance_id())) + var response = child.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response) + + if child is ConditionLeaf: + blackboard.set_value("last_condition", child, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = child + if child is ActionLeaf: + blackboard.set_value("running_action", child, str(actor.get_instance_id())) + else: + child.after_run(actor, blackboard) + return response + else: + interrupt(actor, blackboard) + child.after_run(actor, blackboard) + return FAILURE + + +func before_run(actor: Node, blackboard: Blackboard) -> void: + blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id())) + if get_child_count() > 0: + get_child(0).before_run(actor, blackboard) + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"TimeLimiterDecorator") + return classes + + +func _get_configuration_warnings() -> PackedStringArray: + if not get_child_count() == 1: + return ["Requires exactly one child node"] + return [] diff --git a/addons/beehave/nodes/decorators/until_fail.gd b/addons/beehave/nodes/decorators/until_fail.gd new file mode 100644 index 0000000..8f5ebbe --- /dev/null +++ b/addons/beehave/nodes/decorators/until_fail.gd @@ -0,0 +1,33 @@ +@tool +@icon("../../icons/until_fail.svg") +class_name UntilFailDecorator +extends Decorator + +## The UntilFail Decorator will return `RUNNING` if its child returns +## `SUCCESS` or `RUNNING` or it will return `SUCCESS` if its child returns +## `FAILURE` + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var c = get_child(0) + + if c != running_child: + c.before_run(actor, blackboard) + + var response = c.tick(actor, blackboard) + if can_send_message(blackboard): + BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response) + + if c is ConditionLeaf: + blackboard.set_value("last_condition", c, str(actor.get_instance_id())) + blackboard.set_value("last_condition_status", response, str(actor.get_instance_id())) + + if response == RUNNING: + running_child = c + if c is ActionLeaf: + blackboard.set_value("running_action", c, str(actor.get_instance_id())) + return RUNNING + if response == SUCCESS: + 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..9074c07 --- /dev/null +++ b/addons/beehave/nodes/leaves/action.gd @@ -0,0 +1,14 @@ +@tool +@icon("../../icons/action.svg") +class_name ActionLeaf extends Leaf + +## Actions are leaf nodes that define a task to be performed by an actor. +## Their execution can be long running, potentially being called across multiple +## frame executions. In this case, the node should return `RUNNING` until the +## action is completed. + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"ActionLeaf") + return classes diff --git a/addons/beehave/nodes/leaves/blackboard_compare.gd b/addons/beehave/nodes/leaves/blackboard_compare.gd new file mode 100644 index 0000000..38a870d --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_compare.gd @@ -0,0 +1,65 @@ +@tool +class_name BlackboardCompareCondition extends ConditionLeaf + +## Compares two values using the specified comparison operator. +## Returns [code]FAILURE[/code] if any of the expression fails or the +## comparison operation returns [code]false[/code], otherwise it returns [code]SUCCESS[/code]. + +enum Operators { + EQUAL, + NOT_EQUAL, + GREATER, + LESS, + GREATER_EQUAL, + LESS_EQUAL, +} + +## Expression represetning left operand. +## This value can be any valid GDScript expression. +## In order to use the existing blackboard keys for comparison, +## use get_value("key_name") e.g. get_value("direction").length() +@export_placeholder(EXPRESSION_PLACEHOLDER) var left_operand: String = "" +## Comparison operator. +@export_enum("==", "!=", ">", "<", ">=", "<=") var operator: int = 0 +## Expression represetning right operand. +## This value can be any valid GDScript expression. +## In order to use the existing blackboard keys for comparison, +## use get_value("key_name") e.g. get_value("direction").length() +@export_placeholder(EXPRESSION_PLACEHOLDER) var right_operand: String = "" + +@onready var _left_expression: Expression = _parse_expression(left_operand) +@onready var _right_expression: Expression = _parse_expression(right_operand) + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var left: Variant = _left_expression.execute([], blackboard) + + if _left_expression.has_execute_failed(): + return FAILURE + + var right: Variant = _right_expression.execute([], blackboard) + + if _right_expression.has_execute_failed(): + return FAILURE + + var result: bool = false + + match operator: + Operators.EQUAL: + result = left == right + Operators.NOT_EQUAL: + result = left != right + Operators.GREATER: + result = left > right + Operators.LESS: + result = left < right + Operators.GREATER_EQUAL: + result = left >= right + Operators.LESS_EQUAL: + result = left <= right + + return SUCCESS if result else FAILURE + + +func _get_expression_sources() -> Array[String]: + return [left_operand, right_operand] diff --git a/addons/beehave/nodes/leaves/blackboard_erase.gd b/addons/beehave/nodes/leaves/blackboard_erase.gd new file mode 100644 index 0000000..e5cc1d4 --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_erase.gd @@ -0,0 +1,25 @@ +@tool +class_name BlackboardEraseAction extends ActionLeaf + +## Erases the specified key from the blackboard. +## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code]. + +## Expression representing a blackboard key. +@export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = "" + +@onready var _key_expression: Expression = _parse_expression(key) + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var key_value: Variant = _key_expression.execute([], blackboard) + + if _key_expression.has_execute_failed(): + return FAILURE + + blackboard.erase_value(key_value) + + return SUCCESS + + +func _get_expression_sources() -> Array[String]: + return [key] diff --git a/addons/beehave/nodes/leaves/blackboard_has.gd b/addons/beehave/nodes/leaves/blackboard_has.gd new file mode 100644 index 0000000..2ee5e92 --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_has.gd @@ -0,0 +1,23 @@ +@tool +class_name BlackboardHasCondition extends ConditionLeaf + +## Returns [code]FAILURE[/code] if expression execution fails or the specified key doesn't exist. +## Returns [code]SUCCESS[/code] if blackboard has the specified key. + +## Expression representing a blackboard key. +@export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = "" + +@onready var _key_expression: Expression = _parse_expression(key) + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var key_value: Variant = _key_expression.execute([], blackboard) + + if _key_expression.has_execute_failed(): + return FAILURE + + return SUCCESS if blackboard.has_value(key_value) else FAILURE + + +func _get_expression_sources() -> Array[String]: + return [key] diff --git a/addons/beehave/nodes/leaves/blackboard_set.gd b/addons/beehave/nodes/leaves/blackboard_set.gd new file mode 100644 index 0000000..4f0ed9f --- /dev/null +++ b/addons/beehave/nodes/leaves/blackboard_set.gd @@ -0,0 +1,33 @@ +@tool +class_name BlackboardSetAction extends ActionLeaf + +## Sets the specified key to the specified value. +## Returns [code]FAILURE[/code] if expression execution fails, otherwise [code]SUCCESS[/code]. + +## Expression representing a blackboard key. +@export_placeholder(EXPRESSION_PLACEHOLDER) var key: String = "" +## Expression representing a blackboard value to assign to the specified key. +@export_placeholder(EXPRESSION_PLACEHOLDER) var value: String = "" + +@onready var _key_expression: Expression = _parse_expression(key) +@onready var _value_expression: Expression = _parse_expression(value) + + +func tick(actor: Node, blackboard: Blackboard) -> int: + var key_value: Variant = _key_expression.execute([], blackboard) + + if _key_expression.has_execute_failed(): + return FAILURE + + var value_value: Variant = _value_expression.execute([], blackboard) + + if _value_expression.has_execute_failed(): + return FAILURE + + blackboard.set_value(key_value, value_value) + + return SUCCESS + + +func _get_expression_sources() -> Array[String]: + return [key, value] diff --git a/addons/beehave/nodes/leaves/condition.gd b/addons/beehave/nodes/leaves/condition.gd new file mode 100644 index 0000000..f4610b4 --- /dev/null +++ b/addons/beehave/nodes/leaves/condition.gd @@ -0,0 +1,12 @@ +@tool +@icon("../../icons/condition.svg") +class_name ConditionLeaf extends Leaf + +## Conditions are leaf nodes that either return SUCCESS or FAILURE depending on +## a single simple condition. They should never return `RUNNING`. + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"ConditionLeaf") + return classes diff --git a/addons/beehave/nodes/leaves/leaf.gd b/addons/beehave/nodes/leaves/leaf.gd new file mode 100644 index 0000000..4946c7d --- /dev/null +++ b/addons/beehave/nodes/leaves/leaf.gd @@ -0,0 +1,48 @@ +@tool +@icon("../../icons/category_leaf.svg") +class_name Leaf extends BeehaveNode + +## Base class for all leaf nodes of the tree. + +const EXPRESSION_PLACEHOLDER: String = "Insert an expression..." + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = [] + + var children: Array[Node] = get_children() + + if children.any(func(x): return x is BeehaveNode): + warnings.append("Leaf nodes should not have any child nodes. They won't be ticked.") + + for source in _get_expression_sources(): + var error_text: String = _parse_expression(source).get_error_text() + if not error_text.is_empty(): + warnings.append("Expression `%s` is invalid! Error text: `%s`" % [source, error_text]) + + return warnings + + +func _parse_expression(source: String) -> Expression: + var result: Expression = Expression.new() + var error: int = result.parse(source) + + if not Engine.is_editor_hint() and error != OK: + push_error( + ( + "[Leaf] Couldn't parse expression with source: `%s` Error text: `%s`" + % [source, result.get_error_text()] + ) + ) + + return result + + +func _get_expression_sources() -> Array[String]: # virtual + return [] + + +func get_class_name() -> Array[StringName]: + var classes := super() + classes.push_back(&"Leaf") + return classes diff --git a/addons/beehave/plugin.cfg b/addons/beehave/plugin.cfg new file mode 100644 index 0000000..f06defe --- /dev/null +++ b/addons/beehave/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Beehave" +description="🐝 Behavior Tree addon for Godot Engine" +author="bitbrain" +version="2.8.2-dev" +script="plugin.gd" diff --git a/addons/beehave/plugin.gd b/addons/beehave/plugin.gd new file mode 100644 index 0000000..cda3b0d --- /dev/null +++ b/addons/beehave/plugin.gd @@ -0,0 +1,26 @@ +@tool +extends EditorPlugin + +const BeehaveEditorDebugger := preload("debug/debugger.gd") +var editor_debugger: BeehaveEditorDebugger +var frames: RefCounted + + +func _init(): + name = "BeehavePlugin" + add_autoload_singleton("BeehaveGlobalMetrics", "metrics/beehave_global_metrics.gd") + add_autoload_singleton("BeehaveGlobalDebugger", "debug/global_debugger.gd") + print("Beehave initialized!") + + +func _enter_tree() -> void: + editor_debugger = BeehaveEditorDebugger.new() + if Engine.get_version_info().minor >= 2: + frames = preload("debug/new_frames.gd").new() + else: + frames = preload("debug/old_frames.gd").new() + add_debugger_plugin(editor_debugger) + + +func _exit_tree() -> void: + remove_debugger_plugin(editor_debugger) diff --git a/addons/beehave/utils/utils.gd b/addons/beehave/utils/utils.gd new file mode 100644 index 0000000..5f51ce7 --- /dev/null +++ b/addons/beehave/utils/utils.gd @@ -0,0 +1,21 @@ +@tool + + +static func get_plugin() -> EditorPlugin: + var tree: SceneTree = Engine.get_main_loop() + return tree.get_root().get_child(0).get_node_or_null("BeehavePlugin") + + +static func get_editor_scale() -> float: + var plugin := get_plugin() + if plugin: + return plugin.get_editor_interface().get_editor_scale() + return 1.0 + + +static func get_frames() -> RefCounted: + var plugin := get_plugin() + if plugin: + return plugin.frames + push_error("Can't find Beehave Plugin") + return null diff --git a/autoload/database/database.gd b/autoload/database/database.gd new file mode 100644 index 0000000..9d48022 --- /dev/null +++ b/autoload/database/database.gd @@ -0,0 +1,33 @@ +extends Node + +#不同族群单位之间的初始好感度 +#下面意思为测试族群1对2具有初始负面好感 +#相反2对1具有正面好感 +var init_favour:Dictionary={ + #测试族群1 + "test_1":{ + #对测试族群2的初始好感度为-10 + "test_1":20, + "test_2":-50, + "player":-20, + }, + "test_2":{ + + "test_2":100, + "test_1":50, + "player":-20, + + } +} +#获取初始好感度 +func get_init_favour(self_type:String,other_type:String): + if not init_favour.has(self_type): + return 0 + var favour_dic=init_favour[self_type] + if not favour_dic.has(other_type): + return 0 + else: + return favour_dic[other_type] + + + pass diff --git a/autoload/database/database.tscn b/autoload/database/database.tscn new file mode 100644 index 0000000..ec32be8 --- /dev/null +++ b/autoload/database/database.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://ds0gdslpfq47i"] + +[ext_resource type="Script" path="res://autoload/database/database.gd" id="1_5ljwu"] + +[node name="database" type="Node"] +script = ExtResource("1_5ljwu") diff --git a/autoload/global/global.gd b/autoload/global/global.gd new file mode 100644 index 0000000..a7988e3 --- /dev/null +++ b/autoload/global/global.gd @@ -0,0 +1,47 @@ +extends Node +#保存ID对应角色实例的字典 +var unit_instance_dic:Dictionary={ + +} +#可交互物品/建筑的类型:Array字典 +var item_type_dic_arr:Dictionary={ + +} +#设置对象实例 +func set_unit_instance(id:String,instance:Node): + if unit_instance_dic.has(id): + var before_instance=unit_instance_dic[id] + if is_instance_valid(before_instance): + before_instance.queue_free() + unit_instance_dic[id]=instance +#获取对象实例 +func get_unit_instance(id:String): + if unit_instance_dic.has(id) and is_instance_valid(unit_instance_dic[id]): + return unit_instance_dic[id] + else: + return null +#单位好感度字典 +var unit_favour_dic:Dictionary={ + +} +#获取与对应单位的好感度,如果不存在则根据双方族群自动创建初始好感 +func get_unit_favour(self_id:String,other_id:String): + + if not unit_favour_dic.has(self_id): + var favour=Database.get_init_favour(get_unit_instance(self_id).unit_type,get_unit_instance(other_id).unit_type) + unit_favour_dic[self_id]={other_id:favour} + return favour + elif not unit_favour_dic[self_id].has(other_id): + var favour=Database.get_init_favour(get_unit_instance(self_id).unit_type,get_unit_instance(other_id).unit_type) + unit_favour_dic[self_id][other_id]=favour + return favour + else: + return unit_favour_dic[self_id][other_id] + pass + +#设置好感度 +func set_unit_favour(self_id:String,other_id:String,favour:float): + if not unit_favour_dic.has(self_id): + unit_favour_dic[self_id]={other_id:favour} + else: + unit_favour_dic[self_id][other_id]=favour diff --git a/autoload/global/global.tscn b/autoload/global/global.tscn new file mode 100644 index 0000000..3a66e4d --- /dev/null +++ b/autoload/global/global.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bbovfa26lay8e"] + +[ext_resource type="Script" path="res://autoload/global/global.gd" id="1_7m8su"] + +[node name="global" type="Node"] +script = ExtResource("1_7m8su") diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..dc342ea --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://csk8u15wepd1w" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..2981d1b --- /dev/null +++ b/project.godot @@ -0,0 +1,50 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="模拟异世界" +run/main_scene="res://scene/test/test_main.tscn" +config/features=PackedStringArray("4.3", "Forward Plus") +boot_splash/bg_color=Color(0, 0, 0, 0) +config/icon="res://icon.svg" + +[autoload] + +BeehaveGlobalMetrics="*res://addons/beehave/metrics/beehave_global_metrics.gd" +BeehaveGlobalDebugger="*res://addons/beehave/debug/global_debugger.gd" +Database="*res://autoload/database/database.tscn" +Global="*res://autoload/global/global.tscn" + +[display] + +window/size/initial_position_type=2 +window/size/initial_screen=1 + +[editor_plugins] + +enabled=PackedStringArray("res://addons/beehave/plugin.cfg") + +[input] + +mouse_left={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(297, 21),"global_position":Vector2(306, 67),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} +mouse_right={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(150, 28),"global_position":Vector2(159, 74),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} + +[rendering] + +environment/defaults/default_clear_color=Color(0.301961, 0.301961, 0.301961, 0) diff --git a/res/animation/other_character_default.tres b/res/animation/other_character_default.tres new file mode 100644 index 0000000..2092e28 --- /dev/null +++ b/res/animation/other_character_default.tres @@ -0,0 +1,413 @@ +[gd_resource type="SpriteFrames" load_steps=53 format=3 uid="uid://jbsg1umeffu1"] + +[ext_resource type="Texture2D" uid="uid://b8mr80al21rka" path="res://TEST/mystic_woods_free_2.2/sprites/characters/player.png" id="1_ciqw4"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_15036"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 432, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_l0qjt"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 432, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jv6ku"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 432, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_p5ts7"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 144, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_fdekw"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 144, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_uc0b0"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 144, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_v0bwb"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 144, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_sfgu1"] +atlas = ExtResource("1_ciqw4") +region = Rect2(192, 144, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_pkr8f"] +atlas = ExtResource("1_ciqw4") +region = Rect2(240, 144, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dehdu"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 288, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_fs2e1"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 288, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1swd0"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 288, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0nfde"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 288, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_7lq3f"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_22e3l"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_7r7es"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qla58"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_j4fw1"] +atlas = ExtResource("1_ciqw4") +region = Rect2(192, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_efry1"] +atlas = ExtResource("1_ciqw4") +region = Rect2(240, 0, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_8w6ce"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 192, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_7lbf3"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 192, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_kguei"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 192, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dwllq"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 192, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_25nxc"] +atlas = ExtResource("1_ciqw4") +region = Rect2(192, 192, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0erop"] +atlas = ExtResource("1_ciqw4") +region = Rect2(240, 192, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3tqjp"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 336, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_u5img"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 336, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_p1dr4"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 336, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3judn"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 336, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_wm7vq"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 48, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_j8ewr"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 48, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ubgqn"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 48, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_algwd"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 48, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_18b3y"] +atlas = ExtResource("1_ciqw4") +region = Rect2(192, 48, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_wxhnb"] +atlas = ExtResource("1_ciqw4") +region = Rect2(240, 48, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_cut56"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 240, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_41mkh"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 240, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ixxhs"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 240, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_in33l"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 240, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_vwt1n"] +atlas = ExtResource("1_ciqw4") +region = Rect2(192, 240, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_w86to"] +atlas = ExtResource("1_ciqw4") +region = Rect2(240, 240, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_67mfl"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 384, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_lan5a"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 384, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_kwvch"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 384, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_5nl1s"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 384, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_k2iw0"] +atlas = ExtResource("1_ciqw4") +region = Rect2(0, 96, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_vbv3o"] +atlas = ExtResource("1_ciqw4") +region = Rect2(48, 96, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_df8uv"] +atlas = ExtResource("1_ciqw4") +region = Rect2(96, 96, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ts0ik"] +atlas = ExtResource("1_ciqw4") +region = Rect2(144, 96, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_oi8qt"] +atlas = ExtResource("1_ciqw4") +region = Rect2(192, 96, 48, 48) + +[sub_resource type="AtlasTexture" id="AtlasTexture_725dq"] +atlas = ExtResource("1_ciqw4") +region = Rect2(240, 96, 48, 48) + +[resource] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_15036") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_l0qjt") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jv6ku") +}], +"loop": false, +"name": &"dead", +"speed": 10.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_p5ts7") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fdekw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_uc0b0") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_v0bwb") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_sfgu1") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_pkr8f") +}], +"loop": true, +"name": &"down", +"speed": 10.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_dehdu") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fs2e1") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_1swd0") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_0nfde") +}], +"loop": false, +"name": &"down_attack", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_7lq3f") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_22e3l") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_7r7es") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qla58") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_j4fw1") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_efry1") +}], +"loop": true, +"name": &"down_idle", +"speed": 10.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_8w6ce") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_7lbf3") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_kguei") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_dwllq") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_25nxc") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_0erop") +}], +"loop": true, +"name": &"left_right", +"speed": 10.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_3tqjp") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_u5img") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_p1dr4") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3judn") +}], +"loop": false, +"name": &"left_right_attack", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_wm7vq") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_j8ewr") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ubgqn") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_algwd") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_18b3y") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_wxhnb") +}], +"loop": true, +"name": &"left_right_idle", +"speed": 10.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_cut56") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_41mkh") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ixxhs") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_in33l") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_vwt1n") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_w86to") +}], +"loop": true, +"name": &"up", +"speed": 10.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_67mfl") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_lan5a") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_kwvch") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_5nl1s") +}], +"loop": false, +"name": &"up_attack", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_k2iw0") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_vbv3o") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_df8uv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ts0ik") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_oi8qt") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_725dq") +}], +"loop": true, +"name": &"up_idle", +"speed": 10.0 +}] diff --git a/res/image/test/tile.png b/res/image/test/tile.png new file mode 100644 index 0000000..f6dd89f Binary files /dev/null and b/res/image/test/tile.png differ diff --git a/res/image/test/tile.png.import b/res/image/test/tile.png.import new file mode 100644 index 0000000..a9499b9 --- /dev/null +++ b/res/image/test/tile.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dxtu12mw7fsq8" +path="res://.godot/imported/tile.png-9aeae6768d5b314413b30957805abe9e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://res/image/test/tile.png" +dest_files=["res://.godot/imported/tile.png-9aeae6768d5b314413b30957805abe9e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scene/behavour_tree/action_leaf.gd b/scene/behavour_tree/action_leaf.gd new file mode 100644 index 0000000..bb7b6a6 --- /dev/null +++ b/scene/behavour_tree/action_leaf.gd @@ -0,0 +1 @@ +extends ActionLeaf diff --git a/scene/behavour_tree/await_time.gd b/scene/behavour_tree/await_time.gd new file mode 100644 index 0000000..40343b6 --- /dev/null +++ b/scene/behavour_tree/await_time.gd @@ -0,0 +1,3 @@ +extends SequenceComposite +##等待 +@export var await_time:float diff --git a/scene/behavour_tree/beehave_tree.gd b/scene/behavour_tree/beehave_tree.gd new file mode 100644 index 0000000..eedc09e --- /dev/null +++ b/scene/behavour_tree/beehave_tree.gd @@ -0,0 +1 @@ +extends BeehaveTree diff --git a/scene/behavour_tree/beehave_tree.tscn b/scene/behavour_tree/beehave_tree.tscn new file mode 100644 index 0000000..ae0fc6b --- /dev/null +++ b/scene/behavour_tree/beehave_tree.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=7 format=3 uid="uid://w2jggqirpb3q"] + +[ext_resource type="Script" path="res://scene/behavour_tree/beehave_tree.gd" id="1_r1nve"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/selector.gd" id="2_hodda"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/sequence.gd" id="3_1mypc"] +[ext_resource type="Script" path="res://addons/beehave/nodes/leaves/condition.gd" id="4_58d1f"] +[ext_resource type="Script" path="res://addons/beehave/nodes/leaves/action.gd" id="5_6ttu2"] +[ext_resource type="Script" path="res://scene/behavour_tree/action_leaf.gd" id="5_woxrp"] + +[node name="BeehaveTree" type="Node" node_paths=PackedStringArray("blackboard")] +script = ExtResource("1_r1nve") +blackboard = NodePath("") + +[node name="SelectorComposite" type="Node" parent="."] +script = ExtResource("2_hodda") + +[node name="SequenceComposite" type="Node" parent="SelectorComposite"] +script = ExtResource("3_1mypc") + +[node name="ConditionLeaf" type="Node" parent="SelectorComposite/SequenceComposite"] +script = ExtResource("4_58d1f") + +[node name="ActionLeaf" type="Node" parent="SelectorComposite/SequenceComposite"] +script = ExtResource("5_woxrp") + +[node name="SequenceComposite2" type="Node" parent="SelectorComposite"] +script = ExtResource("3_1mypc") + +[node name="ConditionLeaf" type="Node" parent="SelectorComposite/SequenceComposite2"] +script = ExtResource("4_58d1f") + +[node name="ActionLeaf" type="Node" parent="SelectorComposite/SequenceComposite2"] +script = ExtResource("5_6ttu2") diff --git a/scene/behavour_tree/condition_accuse.gd b/scene/behavour_tree/condition_accuse.gd new file mode 100644 index 0000000..19b1bc8 --- /dev/null +++ b/scene/behavour_tree/condition_accuse.gd @@ -0,0 +1,22 @@ +extends ConditionLeaf + + +func tick(actor:Node,black_board:Blackboard): + var unit:UnitOther=actor + var all_bodys=unit.sense_area.get_overlapping_bodies() + for i in all_bodys: + if i is Unit: + var favour=Global.get_unit_favour(unit.unit_id,i.unit_id) + var res=get_res(favour,unit.get_hungry()) + if res: + black_board.set_value("target_unit_id",i.unit_id) + return SUCCESS + return FAILURE + pass + +#返回true口角判定成功 +func get_res(favour:float,hungry:float=0)->bool: + if favour>0 or favour<-50: + return false + else: + return randf()<=(-favour/100)-hungry/300 diff --git a/scene/character/dead_scene_sprite/dead_scene.gd b/scene/character/dead_scene_sprite/dead_scene.gd new file mode 100644 index 0000000..53ac13d --- /dev/null +++ b/scene/character/dead_scene_sprite/dead_scene.gd @@ -0,0 +1,18 @@ +extends AnimatedSprite2D +@onready var animation_player: AnimationPlayer = $AnimationPlayer + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + + +func _on_animation_finished() -> void: + animation_player.play("mddulutehide") + pass # Replace with function body. + + +func _on_animation_player_animation_finished(anim_name: StringName) -> void: + queue_free() + pass # Replace with function body. diff --git a/scene/character/dead_scene_sprite/dead_scene.tscn b/scene/character/dead_scene_sprite/dead_scene.tscn new file mode 100644 index 0000000..41bda40 --- /dev/null +++ b/scene/character/dead_scene_sprite/dead_scene.tscn @@ -0,0 +1,51 @@ +[gd_scene load_steps=5 format=3 uid="uid://dey5n3rj0355t"] + +[ext_resource type="Script" path="res://scene/character/dead_scene_sprite/dead_scene.gd" id="1_ugaxh"] + +[sub_resource type="Animation" id="Animation_w6wlf"] +resource_name = "modulutehide" +length = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.5), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} + +[sub_resource type="Animation" id="Animation_mdkhy"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_a2cak"] +_data = { +"RESET": SubResource("Animation_mdkhy"), +"modulutehide": SubResource("Animation_w6wlf") +} + +[node name="DeadScene" type="AnimatedSprite2D"] +script = ExtResource("1_ugaxh") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_a2cak") +} + +[connection signal="animation_finished" from="." to="." method="_on_animation_finished"] +[connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_player_animation_finished"] diff --git a/scene/class/food.gd b/scene/class/food.gd new file mode 100644 index 0000000..ebb667b --- /dev/null +++ b/scene/class/food.gd @@ -0,0 +1,17 @@ +extends Area2D +class_name Food + +var hp:float=100 +var hungry:float=100 + + + +func _ready() -> void: + + pass + + +#被吃掉,比如如果要建立一个全局,可以用这个函数删掉 +func eated(): + + self.queue_free() diff --git a/scene/test/action_accuse.gd b/scene/test/action_accuse.gd new file mode 100644 index 0000000..cfc22be --- /dev/null +++ b/scene/test/action_accuse.gd @@ -0,0 +1,10 @@ +extends ActionLeaf + +func tick(actor:Node,black_board:Blackboard): + var unit:UnitOther=actor + if black_board.has_value("target_unit_id"): + unit.accuse(black_board.get_value("target_unit_id")) + black_board.erase_value("target_unit_id") + return SUCCESS + else: + return FAILURE diff --git a/scene/test/action_accuse.tscn b/scene/test/action_accuse.tscn new file mode 100644 index 0000000..83c215b --- /dev/null +++ b/scene/test/action_accuse.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://c3pouwk6mbovv"] + +[ext_resource type="Script" path="res://scene/test/action_accuse.gd" id="1_1jbpf"] + +[node name="action_accuse" type="Node"] +script = ExtResource("1_1jbpf") diff --git a/scene/test/action_attack.gd b/scene/test/action_attack.gd new file mode 100644 index 0000000..8f15b95 --- /dev/null +++ b/scene/test/action_attack.gd @@ -0,0 +1,17 @@ +extends ActionLeaf + + +func tick(actor:Node,black_board:Blackboard): + var unit:Unit=actor + if unit.is_attacking(): + var target_unit:Unit=Global.get_unit_instance(black_board.get_value("target_unit_id")) + unit.set_target(target_unit.global_position) + return RUNNING + if unit.is_attack_finished(): + black_board.erase_value("target_unit_id") + return SUCCESS + if black_board.has_value("target_unit_id") and not unit.is_attacking(): + unit.attack() + var target_unit:Unit=Global.get_unit_instance(black_board.get_value("target_unit_id")) + unit.set_target(target_unit.global_position) + return RUNNING diff --git a/scene/test/action_attack.tscn b/scene/test/action_attack.tscn new file mode 100644 index 0000000..639b2ae --- /dev/null +++ b/scene/test/action_attack.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bcr5oa32fvxwa"] + +[ext_resource type="Script" path="res://scene/test/action_attack.gd" id="1_ty0vr"] + +[node name="action_attack" type="Node"] +script = ExtResource("1_ty0vr") diff --git a/scene/test/action_await_time.gd b/scene/test/action_await_time.gd new file mode 100644 index 0000000..b24ec29 --- /dev/null +++ b/scene/test/action_await_time.gd @@ -0,0 +1,16 @@ +extends ActionLeaf + +@onready var timer: Timer = $Timer +var is_self_started:bool=false +func tick(actor:Node,blackboard:Blackboard): + if not is_self_started and timer.is_stopped(): + timer.start(get_parent().await_time) + is_self_started=true + return RUNNING + elif is_self_started and not timer.is_stopped(): + return RUNNING + elif is_self_started and timer.is_stopped(): + is_self_started=false + return SUCCESS + else: + return FAILED diff --git a/scene/test/action_eat.gd b/scene/test/action_eat.gd new file mode 100644 index 0000000..2b1dfd4 --- /dev/null +++ b/scene/test/action_eat.gd @@ -0,0 +1,12 @@ +extends ActionLeaf + +func tick(actor:Node,black_board:Blackboard): + var unit:Unit=actor + if black_board.has_value("target"): + + var target=black_board.get_value("target") + if target is Food and unit.is_unit_instance_in_touch_area(target) : + unit.eat(target) + return SUCCESS + return FAILURE + return FAILURE diff --git a/scene/test/action_eat.tscn b/scene/test/action_eat.tscn new file mode 100644 index 0000000..3e78511 --- /dev/null +++ b/scene/test/action_eat.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://k7sldgn2joe0"] + +[ext_resource type="Script" path="res://scene/test/action_eat.gd" id="1_ahrfw"] + +[node name="action_eat" type="Node"] +script = ExtResource("1_ahrfw") diff --git a/scene/test/action_move_to_target.gd b/scene/test/action_move_to_target.gd new file mode 100644 index 0000000..0728da6 --- /dev/null +++ b/scene/test/action_move_to_target.gd @@ -0,0 +1,64 @@ +extends ActionLeaf + +#执行成功后是否抹除黑板目标数据 +@export var should_erase_target:bool=false + +#用于移动到特定单位的可交互范围内 +func tick(actor:Node,black_board:Blackboard): + var unit:Unit=actor + if black_board.has_value("target"): + var target=black_board.get_value("target") + if not is_instance_valid(target) or not target is Node2D: + black_board.erase_value("target") + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + unit.stop_move() + return FAILURE + #获取要移动到的目标 + if unit.is_unit_instance_in_touch_area(target): + if should_erase_target: + black_board.erase_value("target") + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + unit.stop_move() + return SUCCESS + unit.set_target_pos(target.global_position) + match unit.get_dir(): + 0: + unit.play_animation("up") + 1: + unit.play_animation("down") + 2: + unit.play_animation("left_right") + 3: + unit.play_animation("left_right") + return RUNNING + else: + black_board.erase_value("target") + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + unit.stop_move() + return FAILURE + + diff --git a/scene/test/action_move_to_target.tscn b/scene/test/action_move_to_target.tscn new file mode 100644 index 0000000..c61e0ac --- /dev/null +++ b/scene/test/action_move_to_target.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://b65amfq474sv7"] + +[ext_resource type="Script" path="res://scene/test/action_move_to_target.gd" id="1_2p57o"] + +[node name="action_move_to_target" type="Node"] +script = ExtResource("1_2p57o") diff --git a/scene/test/action_move_to_unit.gd b/scene/test/action_move_to_unit.gd new file mode 100644 index 0000000..bb33a19 --- /dev/null +++ b/scene/test/action_move_to_unit.gd @@ -0,0 +1,58 @@ +extends ActionLeaf +#执行成功后是否抹除黑板目标数据 +@export var should_erase_target_id:bool=false + +#用于移动到特定单位的可交互范围内 +func tick(actor:Node,black_board:Blackboard): + var unit:Unit=actor + if black_board.has_value("target_unit_id"): + var target_id:String=black_board.get_value("target_unit_id") + if Global.get_unit_instance(target_id)==null: + #移除目标 + black_board.erase_value("target_unit_id") + return FAILURE + #获取要移动到的目标实例 + var target_instance:Unit=Global.get_unit_instance(target_id) + if not unit.is_unit_instance_in_touch_area(target_instance): + unit.set_target_pos(target_instance.global_position) + match unit.get_dir(): + 0: + unit.play_animation("up") + 1: + unit.play_animation("down") + 2: + unit.play_animation("left_right") + 3: + unit.play_animation("left_right") + return RUNNING + else: + unit.stop_move() + if should_erase_target_id: + black_board.erase_value("target_unit_id") + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + return SUCCESS + else: + black_board.erase_value("target_unit_id") + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + return FAILURE + + pass + + + pass diff --git a/scene/test/action_move_to_unit.tscn b/scene/test/action_move_to_unit.tscn new file mode 100644 index 0000000..e7bbf96 --- /dev/null +++ b/scene/test/action_move_to_unit.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://cm7nelq3tbye6"] + +[ext_resource type="Script" path="res://scene/test/action_move_to_unit.gd" id="1_pthy4"] + +[node name="action_move_to_unit" type="Node"] +script = ExtResource("1_pthy4") diff --git a/scene/test/action_set_rand_pos.gd b/scene/test/action_set_rand_pos.gd new file mode 100644 index 0000000..a094bd8 --- /dev/null +++ b/scene/test/action_set_rand_pos.gd @@ -0,0 +1,19 @@ +extends ActionLeaf +##随机进行的距离最大值 +@export var rand_length_max:float=100 +##随机进行的距离最小值 +@export var rand_length_min:float=50 +func tick(actor:Node,black_board:Blackboard): + var unit:Unit=actor + if unit.get_hungry()>50: + rand_length_max=float(unit.get_hungry()-50)/50*2000 + else: + rand_length_max=100 + var now_pos=actor.global_position + #随机距离 + var rand_distance=randf_range(rand_length_min,rand_length_max) + #随机方向 + var rand_dir:Vector2=Vector2(2*randf()-1,2*randf()-1).normalized() + var target_pos=now_pos+rand_dir*rand_distance + black_board.set_value("run_pos",target_pos) + return SUCCESS diff --git a/scene/test/action_set_rand_pos.tscn b/scene/test/action_set_rand_pos.tscn new file mode 100644 index 0000000..e7658b5 --- /dev/null +++ b/scene/test/action_set_rand_pos.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://ldjrnwapxefe"] + +[ext_resource type="Script" path="res://scene/test/action_set_rand_pos.gd" id="1_5ly3l"] + +[node name="action_set_rand_pos" type="Node"] +script = ExtResource("1_5ly3l") diff --git a/scene/test/await_time.tscn b/scene/test/await_time.tscn new file mode 100644 index 0000000..286a8b6 --- /dev/null +++ b/scene/test/await_time.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://jkwn820drv43"] + +[ext_resource type="Script" path="res://scene/behavour_tree/await_time.gd" id="1_g7nf2"] +[ext_resource type="Script" path="res://scene/test/action_await_time.gd" id="2_1ek7g"] + +[node name="await_time" type="Node"] +script = ExtResource("1_g7nf2") + +[node name="action_await_time" type="Node" parent="."] +script = ExtResource("2_1ek7g") + +[node name="Timer" type="Timer" parent="action_await_time"] +one_shot = true diff --git a/scene/test/character.gd b/scene/test/character.gd new file mode 100644 index 0000000..14df247 --- /dev/null +++ b/scene/test/character.gd @@ -0,0 +1,293 @@ +extends CharacterBody2D +class_name Unit +#这里暂时使用中文键debug,方便调试,后会更改 +var action_emoji:Dictionary={ + "生气":"🤯", + "愤怒":"🤬", + "指责":"👈🤯", + "伤害":"😡🔪", + "友好":"🥰", + "被指责":"😟", + "受伤":"😭💔", + "饥饿":"🍽︎", + "好吃":"😋" +} +@export var unit_data:Dictionary={ + "unit_id":"default", + "unit_type":"default", + #动画资源 + "sprite_frames":"res://res/animation/other_character_default.tres", + #动画偏移 + "sprite_offset":Vector2(0,-80), + #动画缩放 + "sprite_scale":Vector2(5,5), + #基础属性 + "hp_base":100, + "atk_base":10, + "speed_base":1, + + + #攻击方式 + "attack_type":"base", + + #下面是临时生成的动态数据 + + #状态值 + #饥饿,疲劳,怒气,紧张,恐慌,压力 + "hungry":0, + "fatigue":0, + "rage":0, + "tensity":0, + + "panic":0, + #压力 + "pressure":0, + + "hp_max":100, + "hp":100, + +} +#允许的状态队列 +var state_value_array:Array=["hungry","fatigue","rage","tensity","panic","pressure","hp_max","hp"] +#设置状态值 +func get_state_value(state_value_name:String): + if state_value_name in state_value_array: + return unit_data[state_value_name] + else: + return null +#状态值改变时发出信号 +signal state_value_changed(state_value_name:String,value) +#设置状态值 +func set_state_value(state_value_name:String,value)->bool: + if state_value_name in state_value_array: + unit_data[state_value_name]=value + state_value_changed.emit(state_value_name,value) + return true + else: + return false +#获取单位ID +func get_unit_id(): + return unit_data["unit_id"] +#获取单位类型 +func get_unit_type(): + return unit_data["unit_type"] + +#快捷获取 +func get_hungry(): + return get_state_value("hungry") +func set_hungry(value): + return set_state_value("hungry",value) + + + + + +##单位的独特ID +@export var unit_id:String="default" +##单位所属族群 +@export var unit_type:String="default" +##每秒触发的timer,用于计算饥饿值积累等 +var second_timer:Timer +@export var animation:AnimatedSprite2D +const unit_speed = 300.0 +const JUMP_VELOCITY = -400.0 +@export var agent: NavigationAgent2D +@export var state_machine: StateMachine +#旋转中心轴 +@export var rotate: Node2D + +##与其他单位交互的范围,目标进入此范围内才进行交互 +@export var touch_area:Area2D +##感知范围 +@export var sense_area:Area2D + +@export var attack_area:Area2D + + + + + + +#var hungry:float=0: + #set(val): + #hungry=val + #if %hungry!=null: + #%hungry.text="饥饿值:"+str(val) +func _ready() -> void: + Global.set_unit_instance(unit_id,self) + agent.max_speed=unit_speed + agent.velocity_computed.connect(safe_speed) + if animation!=null: + animation.frame_changed.connect(frame_changed) + second_timer=Timer.new() + second_timer.autostart=true + second_timer.one_shot=false + second_timer.wait_time=1 + second_timer.timeout.connect(second_timer_time_out) + add_child(second_timer) + + var new_sprite_animation:AnimatedSprite2D=AnimatedSprite2D.new() + new_sprite_animation.sprite_frames=load(unit_data["sprite_frames"]) + + add_child(new_sprite_animation) + new_sprite_animation.position=unit_data["sprite_offset"] + new_sprite_animation.scale=unit_data["sprite_scale"] + animation=new_sprite_animation + + #state_machine.launch() +func set_target_pos(target:Vector2): + agent.target_position=target + +func _physics_process(delta: float) -> void: + if agent!=null and not is_move_finished(): + var current_pos:Vector2=global_position + var next_pos:Vector2=agent.get_next_path_position() + var new_velocity=current_pos.direction_to(next_pos)*unit_speed + if agent.avoidance_enabled: + agent.set_velocity(new_velocity) + rotate.rotation=velocity.angle() + else: + safe_speed(new_velocity) + rotate.rotation=velocity.angle() + if is_move_finished(): + velocity=Vector2.ZERO + else: + cache_velicity=velocity + if animation!=null: + if velocity.x>0: + animation.flip_h=false + if velocity.x<0: + animation.flip_h=true + + move_and_slide() + +func safe_speed(safe_velocity): + velocity=safe_velocity + +func stop_move(): + set_target_pos(self.global_position) + +func is_move_finished(): + #return agent.is_target_reached() + return agent.is_navigation_finished() + +func sent_message(type:String,value): + state_machine.send_message(type,value) + + +#判断unit实例在不在交互范围内 +func is_unit_instance_in_touch_area(instance:Node): + if touch_area==null: + return false + else: + if instance is PhysicsBody2D: + return instance in touch_area.get_overlapping_bodies() + if instance is Area2D: + return instance in touch_area.get_overlapping_areas() +#指责(口角) +func accuse(unit_id:String): + show_action("指责") + var instance=Global.get_unit_instance(unit_id) + if instance is UnitOther: + instance.accused(self.unit_id) + pass +#被指责,调用 +func accused(by_unit_id:String): + show_action("被指责") + Global.set_unit_favour(unit_id,by_unit_id,Global.get_unit_favour(unit_id,by_unit_id)-10) + pass +@export var attack_frames:int=2 + + +#攻击 +func attack(): + show_action("伤害") + match get_dir(): + 0: + play_animation("up_attack") + 1: + play_animation("down_attack") + 2: + play_animation("left_right_attack") + 3: + play_animation("left_right_attack") + pass + +func frame_changed(): + if animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and animation.frame==attack_frames: + use_attack_damage() +func is_attack_finished(): + if animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and not animation.is_playing(): + return true + else: + return false +func is_attacking(): + return animation.animation in [&"up_attack",&"down_attack",&"left_right_attack"] and animation.is_playing() +#在固定帧使用攻击 +func use_attack_damage(): + + for i in attack_area.get_overlapping_bodies(): + + if i is Unit and i!=self: + i.attacked(unit_id) + + pass +func attacked(by_unit_id:String): + show_action("受伤") + Global.set_unit_favour(unit_id,by_unit_id,Global.get_unit_favour(unit_id,by_unit_id)-20) + + pass +func set_target(global_pos:Vector2): + cache_velicity=global_pos-self.global_position + rotate.look_at(global_pos) + if cache_velicity.x>0: + animation.flip_h=false + if cache_velicity.x<0: + animation.flip_h=true + + pass +func show_action(type:String): + print(type) + if action_emoji.has(type): + %action_show.text=action_emoji[type] + if %action_animation.is_playing(): + %action_animation.stop() + %action_animation.play("show") + + pass +func play_animation(animation_name:String): + if animation !=null: + animation.play(animation_name) + + pass +var cache_velicity:Vector2=Vector2(1,0) +#0上,1下,2左,3右 +func get_dir()->int: + if abs(cache_velicity.y)>abs(cache_velicity.x): + + if cache_velicity.y>0: + return 1 + + else: + return 0 + + else: + if cache_velicity.x>0: + return 3 + + else: + return 2 + pass + pass + +func second_timer_time_out(): + + set_hungry(clamp(get_hungry()+10,0,100)) + + pass +func eat(food:Food): + print("吃") + show_action("好吃") + set_hungry(clamp(get_hungry()-food.hungry,0,100)) + food.eated() + pass diff --git a/scene/test/character.tscn b/scene/test/character.tscn new file mode 100644 index 0000000..4f194f2 --- /dev/null +++ b/scene/test/character.tscn @@ -0,0 +1,136 @@ +[gd_scene load_steps=15 format=3 uid="uid://cf2g2urxaukxb"] + +[ext_resource type="Script" path="res://scene/test/self_character.gd" id="1_d0trv"] +[ext_resource type="Script" path="res://scene/test/state_machine.gd" id="2_v5m4x"] +[ext_resource type="Script" path="res://scene/test/idle.gd" id="3_gcdcj"] +[ext_resource type="Script" path="res://scene/test/run.gd" id="4_c7f2w"] +[ext_resource type="Texture2D" uid="uid://csk8u15wepd1w" path="res://icon.svg" id="5_lkgch"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_740ny"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_7uxj5"] +size = Vector2(41, 34) + +[sub_resource type="Animation" id="Animation_tov45"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("action_show:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="Animation" id="Animation_8eva7"] +resource_name = "show" +length = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("action_show:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.1, 0.5), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [false, true, false] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_a6wya"] +_data = { +"RESET": SubResource("Animation_tov45"), +"show": SubResource("Animation_8eva7") +} + +[sub_resource type="Gradient" id="Gradient_k76nm"] +colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_akajp"] +gradient = SubResource("Gradient_k76nm") + +[sub_resource type="Gradient" id="Gradient_vdy2i"] +colors = PackedColorArray(0, 1, 0, 1, 0, 1, 0, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_jp0r7"] +gradient = SubResource("Gradient_vdy2i") + +[node name="CharacterBody2D" type="CharacterBody2D" node_paths=PackedStringArray("agent", "state_machine", "rotate")] +script = ExtResource("1_d0trv") +agent = NodePath("agent") +state_machine = NodePath("state_machine") +rotate = NodePath("rotate") + +[node name="agent" type="NavigationAgent2D" parent="."] +path_postprocessing = 1 +avoidance_enabled = true +radius = 20.0 +debug_enabled = true + +[node name="Camera2D" type="Camera2D" parent="."] +position_smoothing_enabled = true + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_740ny") + +[node name="state_machine" type="Node" parent="."] +script = ExtResource("2_v5m4x") +init_state = NodePath("idle") +owner_player = NodePath("..") + +[node name="idle" type="Node" parent="state_machine"] +script = ExtResource("3_gcdcj") + +[node name="run" type="Node" parent="state_machine"] +script = ExtResource("4_c7f2w") + +[node name="Node3" type="Node" parent="state_machine"] + +[node name="rotate" type="Node2D" parent="."] + +[node name="attack_area" type="Area2D" parent="rotate"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="rotate/attack_area"] +position = Vector2(20.5, 0) +shape = SubResource("RectangleShape2D_7uxj5") + +[node name="Sprite2D" type="Sprite2D" parent="."] +visible = false +texture = ExtResource("5_lkgch") + +[node name="action_show" type="Label" parent="."] +unique_name_in_owner = true +visible = false +z_index = 1 +offset_left = -40.0 +offset_top = -75.0 +offset_right = 39.0 +offset_bottom = -6.0 +theme_override_font_sizes/font_size = 50 +text = "🤬" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="action_animation" type="AnimationPlayer" parent="."] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_a6wya") +} + +[node name="hp" type="TextureProgressBar" parent="."] +unique_name_in_owner = true +modulate = Color(1, 1, 1, 0.580392) +offset_left = -127.0 +offset_top = -260.0 +offset_right = 129.0 +offset_bottom = -220.0 +nine_patch_stretch = true +texture_under = SubResource("GradientTexture1D_akajp") +texture_progress = SubResource("GradientTexture1D_jp0r7") + +[connection signal="state_value_changed" from="." to="." method="_on_state_value_changed"] diff --git a/scene/test/condition_accuse.tscn b/scene/test/condition_accuse.tscn new file mode 100644 index 0000000..25840e3 --- /dev/null +++ b/scene/test/condition_accuse.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://hgsb64hg78m5"] + +[ext_resource type="Script" path="res://scene/behavour_tree/condition_accuse.gd" id="1_nvtrg"] + +[node name="condition_accuse" type="Node"] +script = ExtResource("1_nvtrg") diff --git a/scene/test/condition_attack.gd b/scene/test/condition_attack.gd new file mode 100644 index 0000000..945a649 --- /dev/null +++ b/scene/test/condition_attack.gd @@ -0,0 +1,20 @@ +extends ConditionLeaf +func tick(actor:Node,black_board:Blackboard): + var unit:UnitOther=actor + var all_bodys=unit.sense_area.get_overlapping_bodies() + for i in all_bodys: + if i is Unit: + var favour=Global.get_unit_favour(unit.unit_id,i.unit_id) + var res=get_res(favour,unit.get_hungry()) + if res: + black_board.set_value("target_unit_id",i.unit_id) + return SUCCESS + return FAILURE + pass + +#返回true攻击判定成功 +func get_res(favour:float,hungry:float=0)->bool: + if favour>-50: + return false + else: + return randf()<=(-favour/100)-hungry/300 diff --git a/scene/test/condition_attack.tscn b/scene/test/condition_attack.tscn new file mode 100644 index 0000000..1b30475 --- /dev/null +++ b/scene/test/condition_attack.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://qrl0pc72e3q6"] + +[ext_resource type="Script" path="res://scene/test/condition_attack.gd" id="1_f0v72"] + +[node name="condition_attack" type="Node"] +script = ExtResource("1_f0v72") diff --git a/scene/test/condition_hungry.gd b/scene/test/condition_hungry.gd new file mode 100644 index 0000000..63e0236 --- /dev/null +++ b/scene/test/condition_hungry.gd @@ -0,0 +1,17 @@ +extends ConditionLeaf + + +func tick(actor:Node,black_board:Blackboard): + var unit:UnitOther=actor + if unit.get_hungry()<=50: + return FAILURE + if randf()>float(unit.get_hungry()-50)/50: + return FAILURE + unit.show_action("饥饿") + var all_bodys=unit.sense_area.get_overlapping_areas() + for i in all_bodys: + if i is Food: + black_board.set_value("target",i) + return SUCCESS + return FAILURE + pass diff --git a/scene/test/condition_hungry.tscn b/scene/test/condition_hungry.tscn new file mode 100644 index 0000000..0e95547 --- /dev/null +++ b/scene/test/condition_hungry.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dukqa6ha8aeic"] + +[ext_resource type="Script" path="res://scene/test/condition_hungry.gd" id="1_7gs6t"] + +[node name="condition_hungry" type="Node"] +script = ExtResource("1_7gs6t") diff --git a/scene/test/food.tscn b/scene/test/food.tscn new file mode 100644 index 0000000..2310944 --- /dev/null +++ b/scene/test/food.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=3 format=3 uid="uid://b4mawovxv402a"] + +[ext_resource type="Script" path="res://scene/class/food.gd" id="1_3mwrm"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_q10t5"] +radius = 55.5428 + +[node name="food" type="Area2D"] +script = ExtResource("1_3mwrm") + +[node name="Label" type="Label" parent="."] +offset_left = -71.0 +offset_top = -107.0 +offset_right = 66.0 +offset_bottom = 30.0 +theme_override_font_sizes/font_size = 100 +text = "🍱" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(-3, -40) +shape = SubResource("CircleShape2D_q10t5") diff --git a/scene/test/idle.gd b/scene/test/idle.gd new file mode 100644 index 0000000..a478a19 --- /dev/null +++ b/scene/test/idle.gd @@ -0,0 +1,29 @@ +extends State + + +func enter_state(n): + pass +func update_state(delta): + + pass +func update_state_phy(delta): + match get_player().get_dir(): + 0: + get_player().play_animation("up_idle") + 1: + get_player().play_animation("down_idle") + 2: + get_player().play_animation("left_right_idle") + 3: + get_player().play_animation("left_right_idle") + pass +func exit_state(): + pass + +func process_message(type:String,n): + match type: + "move": + get_player().set_target_pos(n) + change_to_state("run") + + pass diff --git a/scene/test/other_character.gd b/scene/test/other_character.gd new file mode 100644 index 0000000..2c893b3 --- /dev/null +++ b/scene/test/other_character.gd @@ -0,0 +1,27 @@ +extends Unit +#除了玩家外的单位 +class_name UnitOther + + + +func _on_sense_area_body_entered(body: Node2D) -> void: + if body is Unit: + #自动初始化好感度 + Global.get_unit_favour(unit_id,body.unit_id) + pass # Replace with function body. + +func _ready() -> void: + super._ready() + %hp.max_value=unit_data["hp_max"] + %hp.value=unit_data["hp"] + + +func _on_state_value_changed(state_value_name: String, value: Variant) -> void: + match state_value_name: + "hungry": + %hungry.text="饥饿值:"+str(value) + "hp": + %hp.value=value + "hp_max": + %hp.max_value=value + pass # Replace with function body. diff --git a/scene/test/other_character.tscn b/scene/test/other_character.tscn new file mode 100644 index 0000000..7799e01 --- /dev/null +++ b/scene/test/other_character.tscn @@ -0,0 +1,214 @@ +[gd_scene load_steps=28 format=3 uid="uid://dl1axae8yeeeh"] + +[ext_resource type="Script" path="res://scene/test/other_character.gd" id="1_6f8fy"] +[ext_resource type="Texture2D" uid="uid://csk8u15wepd1w" path="res://icon.svg" id="2_tbr4x"] +[ext_resource type="Script" path="res://addons/beehave/nodes/beehave_tree.gd" id="2_twyt5"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/selector.gd" id="3_0pv7j"] +[ext_resource type="PackedScene" uid="uid://ldjrnwapxefe" path="res://scene/test/action_set_rand_pos.tscn" id="3_a6loo"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/sequence.gd" id="3_gehdk"] +[ext_resource type="PackedScene" uid="uid://jkwn820drv43" path="res://scene/test/await_time.tscn" id="4_hl4be"] +[ext_resource type="PackedScene" uid="uid://dg0gnl5n62gq6" path="res://scene/test/run_await.tscn" id="4_hydke"] +[ext_resource type="PackedScene" uid="uid://hgsb64hg78m5" path="res://scene/test/condition_accuse.tscn" id="5_tomxt"] +[ext_resource type="PackedScene" uid="uid://dukqa6ha8aeic" path="res://scene/test/condition_hungry.tscn" id="6_qkap5"] +[ext_resource type="PackedScene" uid="uid://cm7nelq3tbye6" path="res://scene/test/action_move_to_unit.tscn" id="6_wkm0p"] +[ext_resource type="PackedScene" uid="uid://b65amfq474sv7" path="res://scene/test/action_move_to_target.tscn" id="7_phvf1"] +[ext_resource type="PackedScene" uid="uid://c3pouwk6mbovv" path="res://scene/test/action_accuse.tscn" id="7_wxwf6"] +[ext_resource type="PackedScene" uid="uid://k7sldgn2joe0" path="res://scene/test/action_eat.tscn" id="8_xfqbv"] +[ext_resource type="PackedScene" uid="uid://qrl0pc72e3q6" path="res://scene/test/condition_attack.tscn" id="10_85ndl"] +[ext_resource type="PackedScene" uid="uid://bcr5oa32fvxwa" path="res://scene/test/action_attack.tscn" id="11_7tlfn"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_s46hi"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_lphpm"] +size = Vector2(121, 136) + +[sub_resource type="CircleShape2D" id="CircleShape2D_fp36j"] +radius = 104.12 + +[sub_resource type="CircleShape2D" id="CircleShape2D_0d77h"] +radius = 472.662 + +[sub_resource type="Animation" id="Animation_tov45"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("action_show:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="Animation" id="Animation_8eva7"] +resource_name = "show" +length = 0.5 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("action_show:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.1, 0.5), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [false, true, false] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_a6wya"] +_data = { +"RESET": SubResource("Animation_tov45"), +"show": SubResource("Animation_8eva7") +} + +[sub_resource type="Gradient" id="Gradient_uhodq"] +colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_ioncu"] +gradient = SubResource("Gradient_uhodq") + +[sub_resource type="Gradient" id="Gradient_qd3pc"] +colors = PackedColorArray(0, 1, 0, 1, 0, 1, 0, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_6t72h"] +gradient = SubResource("Gradient_qd3pc") + +[node name="other_character" type="CharacterBody2D" node_paths=PackedStringArray("agent", "rotate", "touch_area", "sense_area", "attack_area")] +script = ExtResource("1_6f8fy") +agent = NodePath("agent") +rotate = NodePath("rotate") +touch_area = NodePath("touch_area") +sense_area = NodePath("sense_area") +attack_area = NodePath("rotate/attack_area") + +[node name="Sprite2D" type="Sprite2D" parent="."] +visible = false +texture = ExtResource("2_tbr4x") + +[node name="agent" type="NavigationAgent2D" parent="."] +path_postprocessing = 1 +avoidance_enabled = true +radius = 20.0 +debug_enabled = true + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_s46hi") + +[node name="rotate" type="Node2D" parent="."] + +[node name="attack_area" type="Area2D" parent="rotate"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="rotate/attack_area"] +position = Vector2(60.5, 3) +shape = SubResource("RectangleShape2D_lphpm") + +[node name="BeehaveTree" type="Node" parent="." node_paths=PackedStringArray("blackboard", "actor")] +script = ExtResource("2_twyt5") +blackboard = NodePath("@Node@37976") +actor = NodePath("..") + +[node name="SelectorComposite" type="Node" parent="BeehaveTree"] +script = ExtResource("3_0pv7j") + +[node name="eat" type="Node" parent="BeehaveTree/SelectorComposite"] +script = ExtResource("3_gehdk") + +[node name="condition_hungry" parent="BeehaveTree/SelectorComposite/eat" instance=ExtResource("6_qkap5")] + +[node name="action_move_to_target" parent="BeehaveTree/SelectorComposite/eat" instance=ExtResource("7_phvf1")] + +[node name="action_eat" parent="BeehaveTree/SelectorComposite/eat" instance=ExtResource("8_xfqbv")] + +[node name="accuse" type="Node" parent="BeehaveTree/SelectorComposite"] +script = ExtResource("3_gehdk") + +[node name="condition_accuse" parent="BeehaveTree/SelectorComposite/accuse" instance=ExtResource("5_tomxt")] + +[node name="action_move_to_unit" parent="BeehaveTree/SelectorComposite/accuse" instance=ExtResource("6_wkm0p")] + +[node name="action_accuse" parent="BeehaveTree/SelectorComposite/accuse" instance=ExtResource("7_wxwf6")] + +[node name="await_time" parent="BeehaveTree/SelectorComposite/accuse" instance=ExtResource("4_hl4be")] +await_time = 2.0 + +[node name="attack" type="Node" parent="BeehaveTree/SelectorComposite"] +script = ExtResource("3_gehdk") + +[node name="condition_attack" parent="BeehaveTree/SelectorComposite/attack" instance=ExtResource("10_85ndl")] + +[node name="action_move_to_unit" parent="BeehaveTree/SelectorComposite/attack" instance=ExtResource("6_wkm0p")] + +[node name="action_attack" parent="BeehaveTree/SelectorComposite/attack" instance=ExtResource("11_7tlfn")] + +[node name="await_time" parent="BeehaveTree/SelectorComposite/attack" instance=ExtResource("4_hl4be")] +await_time = 1.0 + +[node name="rand_walk" type="Node" parent="BeehaveTree/SelectorComposite"] +script = ExtResource("3_gehdk") + +[node name="await_time" parent="BeehaveTree/SelectorComposite/rand_walk" instance=ExtResource("4_hl4be")] +await_time = 2.0 + +[node name="action_set_rand_pos" parent="BeehaveTree/SelectorComposite/rand_walk" instance=ExtResource("3_a6loo")] + +[node name="run_await" parent="BeehaveTree/SelectorComposite/rand_walk" instance=ExtResource("4_hydke")] + +[node name="touch_area" type="Area2D" parent="."] +unique_name_in_owner = true + +[node name="CollisionShape2D" type="CollisionShape2D" parent="touch_area"] +shape = SubResource("CircleShape2D_fp36j") + +[node name="sense_area" type="Area2D" parent="."] +unique_name_in_owner = true + +[node name="CollisionShape2D" type="CollisionShape2D" parent="sense_area"] +shape = SubResource("CircleShape2D_0d77h") + +[node name="action_show" type="Label" parent="."] +unique_name_in_owner = true +visible = false +z_index = 1 +offset_left = -40.0 +offset_top = -75.0 +offset_right = 39.0 +offset_bottom = -6.0 +theme_override_font_sizes/font_size = 50 +text = "🤬" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="action_animation" type="AnimationPlayer" parent="."] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_a6wya") +} + +[node name="hungry" type="Label" parent="."] +unique_name_in_owner = true +offset_left = -56.0 +offset_top = -179.0 +offset_right = 48.0 +offset_bottom = -96.0 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_font_sizes/font_size = 20 +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="hp" type="TextureProgressBar" parent="."] +unique_name_in_owner = true +modulate = Color(1, 1, 1, 0.607843) +offset_left = -128.0 +offset_top = -192.0 +offset_right = 128.0 +offset_bottom = -152.0 +nine_patch_stretch = true +texture_under = SubResource("GradientTexture1D_ioncu") +texture_progress = SubResource("GradientTexture1D_6t72h") + +[connection signal="state_value_changed" from="." to="." method="_on_state_value_changed"] +[connection signal="body_entered" from="sense_area" to="." method="_on_sense_area_body_entered"] diff --git a/scene/test/run.gd b/scene/test/run.gd new file mode 100644 index 0000000..607e1d4 --- /dev/null +++ b/scene/test/run.gd @@ -0,0 +1,32 @@ +extends State +func enter_state(n): + pass +func update_state(delta): + pass +func update_state_phy(delta): + if get_player().is_move_finished(): + + change_to_state("idle") + print("切换为idle") + return + + match get_player().get_dir(): + 0: + get_player().play_animation("up") + 1: + get_player().play_animation("down") + 2: + get_player().play_animation("left_right") + 3: + get_player().play_animation("left_right") + pass +func exit_state(): + pass + +func process_message(type:String,n): + match type: + "move": + get_player().set_target_pos(n) + change_to_state("run") + + pass diff --git a/scene/test/run_action.gd b/scene/test/run_action.gd new file mode 100644 index 0000000..f1e14ad --- /dev/null +++ b/scene/test/run_action.gd @@ -0,0 +1,79 @@ +extends ActionLeaf +#当前是否已经开始运动 +var is_started_self:bool=false +@export var time_out_time:float=2 +var is_time_out:bool=false +@onready var timer: Timer = $Timer + +func tick(actor:Node,black_board:Blackboard): + var unit:Unit=actor + if not is_started_self: + if not black_board.has_value("run_pos"): + return FAILURE + var run_pos:Vector2=black_board.get_value("run_pos") + actor.set_target_pos(run_pos) + is_started_self=true + timer.start(time_out_time) + match unit.get_dir(): + 0: + unit.play_animation("up") + 1: + unit.play_animation("down") + 2: + unit.play_animation("left_right") + 3: + unit.play_animation("left_right") + return RUNNING + + else: + if is_time_out: + is_started_self=false + black_board.erase_value("run_pos") + is_time_out=false + timer.stop() + unit.stop_move() + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + return FAILURE + + if actor.is_move_finished(): + is_started_self=false + black_board.erase_value("run_pos") + is_time_out=false + timer.stop() + match unit.get_dir(): + 0: + unit.play_animation("up_idle") + 1: + unit.play_animation("down_idle") + 2: + unit.play_animation("left_right_idle") + 3: + unit.play_animation("left_right_idle") + return SUCCESS + else: + match unit.get_dir(): + 0: + unit.play_animation("up") + 1: + unit.play_animation("down") + 2: + unit.play_animation("left_right") + 3: + unit.play_animation("left_right") + return RUNNING + + + pass + + +func _on_timer_timeout() -> void: + is_time_out=true + pass # Replace with function body. diff --git a/scene/test/run_await.tscn b/scene/test/run_await.tscn new file mode 100644 index 0000000..a14b7dd --- /dev/null +++ b/scene/test/run_await.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=4 format=3 uid="uid://dg0gnl5n62gq6"] + +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/sequence.gd" id="1_qv4d0"] +[ext_resource type="Script" path="res://scene/test/run_condition.gd" id="2_e4qcd"] +[ext_resource type="Script" path="res://scene/test/run_action.gd" id="3_w3x7t"] + +[node name="run_await" type="Node"] +script = ExtResource("1_qv4d0") + +[node name="ConditionLeaf" type="Node" parent="."] +script = ExtResource("2_e4qcd") + +[node name="ActionLeaf" type="Node" parent="."] +script = ExtResource("3_w3x7t") + +[node name="Timer" type="Timer" parent="ActionLeaf"] +wait_time = 10.0 +one_shot = true + +[connection signal="timeout" from="ActionLeaf/Timer" to="ActionLeaf" method="_on_timer_timeout"] diff --git a/scene/test/run_condition.gd b/scene/test/run_condition.gd new file mode 100644 index 0000000..8e00491 --- /dev/null +++ b/scene/test/run_condition.gd @@ -0,0 +1,9 @@ +extends ConditionLeaf + +func tick(actor:Node,black_board:Blackboard): + #如果有设置目标地点 + if black_board.has_value("run_pos"): + + return SUCCESS + else: + return FAILURE diff --git a/scene/test/self_character.gd b/scene/test/self_character.gd new file mode 100644 index 0000000..90b8516 --- /dev/null +++ b/scene/test/self_character.gd @@ -0,0 +1,15 @@ +extends Unit + +func _ready() -> void: + + super._ready() + state_machine.launch() + + +func _on_state_value_changed(state_value_name: String, value: Variant) -> void: + match state_value_name: + "hp": + %hp.value=value + "hp_max": + %hp.max_value=value + pass # Replace with function body. diff --git a/scene/test/state_machine.gd b/scene/test/state_machine.gd new file mode 100644 index 0000000..dc29904 --- /dev/null +++ b/scene/test/state_machine.gd @@ -0,0 +1 @@ +extends StateMachine diff --git a/scene/test/test_main.gd b/scene/test/test_main.gd new file mode 100644 index 0000000..87094c8 --- /dev/null +++ b/scene/test/test_main.gd @@ -0,0 +1,14 @@ +extends Node2D +@export var player: CharacterBody2D + +func _physics_process(delta: float) -> void: + %mouse_finder.position=get_global_mouse_position() + + +func _on_control_gui_input(event: InputEvent) -> void: + if event.is_action_pressed("mouse_right"): + if player: + player.sent_message("move",player.get_global_mouse_position()) + pass + + pass # Replace with function body. diff --git a/scene/test/test_main.tscn b/scene/test/test_main.tscn new file mode 100644 index 0000000..4935546 --- /dev/null +++ b/scene/test/test_main.tscn @@ -0,0 +1,137 @@ +[gd_scene load_steps=10 format=4 uid="uid://1wl1fl3qtxc"] + +[ext_resource type="Script" path="res://scene/test/test_main.gd" id="1_aimp4"] +[ext_resource type="Texture2D" uid="uid://dxtu12mw7fsq8" path="res://res/image/test/tile.png" id="1_jp0cd"] +[ext_resource type="PackedScene" uid="uid://cf2g2urxaukxb" path="res://scene/test/character.tscn" id="2_nvm7o"] +[ext_resource type="PackedScene" uid="uid://b4mawovxv402a" path="res://scene/test/food.tscn" id="3_fgsvv"] +[ext_resource type="PackedScene" uid="uid://dl1axae8yeeeh" path="res://scene/test/other_character.tscn" id="4_eyxcn"] + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_tjudi"] +vertices = PackedVector2Array(32, 32, -32, 32, -32, -32, 32, -32) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(-32, -32, 32, -32, 32, 32, -32, 32)]) +agent_radius = 0.0 + +[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_pe5ig"] +texture = ExtResource("1_jp0cd") +texture_region_size = Vector2i(64, 64) +0:0/0 = 0 +0:0/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_tjudi") +0:1/0 = 0 +0:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(-32, -32, 32, -32, 32, 32, -32, 32) + +[sub_resource type="TileSet" id="TileSet_jmwcd"] +tile_size = Vector2i(64, 64) +physics_layer_0/collision_layer = 1 +navigation_layer_0/layers = 1 +sources/0 = SubResource("TileSetAtlasSource_pe5ig") + +[sub_resource type="CircleShape2D" id="CircleShape2D_shajf"] + +[node name="main" type="Node2D" node_paths=PackedStringArray("player")] +script = ExtResource("1_aimp4") +player = NodePath("CB_add_pos/CharacterBody2D") + +[node name="Node2D" type="Node2D" parent="."] + +[node name="navigation" type="TileMapLayer" parent="Node2D"] +unique_name_in_owner = true +tile_map_data = PackedByteArray("AAD0//r/AAAAAAAAAAD0//v/AAAAAAEAAAD0//z/AAAAAAEAAAD0//3/AAAAAAAAAAD0//7/AAAAAAAAAAD0////AAAAAAAAAAD0/wAAAAAAAAAAAAD0/wEAAAAAAAAAAAD0/wIAAAAAAAAAAAD0/wMAAAAAAAEAAAD0/wQAAAAAAAEAAAD0/wUAAAAAAAAAAAD1/wYAAAAAAAEAAAD2/wYAAAAAAAEAAAD3/wcAAAAAAAAAAAD4/wcAAAAAAAEAAAD5/wcAAAAAAAAAAAD6/wgAAAAAAAEAAAD7/wgAAAAAAAAAAAD8/wgAAAAAAAAAAAD9/wgAAAAAAAAAAAD+/wgAAAAAAAAAAAD+/wkAAAAAAAEAAAD//wkAAAAAAAAAAAAAAAkAAAAAAAAAAAABAAkAAAAAAAAAAAACAAkAAAAAAAAAAAADAAkAAAAAAAAAAAAEAAkAAAAAAAAAAAAFAAkAAAAAAAEAAAAGAAkAAAAAAAEAAAAHAAkAAAAAAAAAAAAIAAkAAAAAAAAAAAAJAAkAAAAAAAAAAAAKAAkAAAAAAAAAAAALAAkAAAAAAAAAAAAMAAkAAAAAAAAAAAAMAAgAAAAAAAAAAAANAAgAAAAAAAAAAAANAAcAAAAAAAAAAAANAAYAAAAAAAAAAAANAAUAAAAAAAAAAAANAAQAAAAAAAAAAAANAAMAAAAAAAAAAAAMAAMAAAAAAAAAAAAMAAIAAAAAAAAAAAAMAAEAAAAAAAAAAAAMAAAAAAAAAAAAAAAMAP//AAAAAAAAAAALAP//AAAAAAAAAAALAP7/AAAAAAAAAAAKAP7/AAAAAAAAAAAKAP3/AAAAAAAAAAAJAP3/AAAAAAAAAAAIAPz/AAAAAAEAAAAHAPz/AAAAAAEAAAAHAPv/AAAAAAAAAAAGAPv/AAAAAAAAAAAFAPv/AAAAAAEAAAAEAPv/AAAAAAEAAAADAPv/AAAAAAEAAAADAPr/AAAAAAAAAAACAPr/AAAAAAAAAAABAPr/AAAAAAAAAAAAAPr/AAAAAAAAAAD///r/AAAAAAAAAAD+//r/AAAAAAAAAAD+//v/AAAAAAAAAAD9//v/AAAAAAAAAAD9//z/AAAAAAAAAAD9//3/AAAAAAAAAAD9//7/AAAAAAAAAAD+//7/AAAAAAAAAAD+////AAAAAAAAAAD+/wAAAAAAAAAAAAD//wAAAAAAAAAAAAD//wEAAAAAAAAAAAD//wIAAAAAAAAAAAD//wMAAAAAAAAAAAD//wQAAAAAAAEAAAAAAAQAAAAAAAAAAAABAAQAAAAAAAAAAAABAAMAAAAAAAAAAAACAAIAAAAAAAAAAADu//f/AAAAAAAAAADu//j/AAAAAAAAAADu//n/AAAAAAAAAADu//r/AAAAAAAAAADu//v/AAAAAAAAAADu//z/AAAAAAAAAADu//3/AAAAAAAAAADu//7/AAAAAAAAAADu////AAAAAAAAAADu/wAAAAAAAAAAAADu/wEAAAAAAAAAAADu/wIAAAAAAAAAAADu/wMAAAAAAAAAAADu/wQAAAAAAAAAAADu/wUAAAAAAAAAAADu/wYAAAAAAAAAAADu/wcAAAAAAAAAAADu/wgAAAAAAAAAAADu/wkAAAAAAAAAAADu/woAAAAAAAAAAADu/wsAAAAAAAAAAADv//f/AAAAAAAAAADv//j/AAAAAAAAAADv//n/AAAAAAAAAADv//r/AAAAAAAAAADv//v/AAAAAAAAAADv//z/AAAAAAAAAADv//3/AAAAAAAAAADv//7/AAAAAAAAAADv////AAAAAAAAAADv/wAAAAAAAAAAAADv/wEAAAAAAAAAAADv/wIAAAAAAAAAAADv/wMAAAAAAAAAAADv/wQAAAAAAAAAAADv/wUAAAAAAAAAAADv/wYAAAAAAAAAAADv/wcAAAAAAAAAAADv/wgAAAAAAAAAAADv/wkAAAAAAAAAAADv/woAAAAAAAAAAADv/wsAAAAAAAAAAADw//f/AAAAAAAAAADw//j/AAAAAAAAAADw//n/AAAAAAAAAADw//r/AAAAAAAAAADw//v/AAAAAAAAAADw//z/AAAAAAAAAADw//3/AAAAAAAAAADw//7/AAAAAAAAAADw////AAAAAAAAAADw/wAAAAAAAAAAAADw/wEAAAAAAAAAAADw/wIAAAAAAAAAAADw/wMAAAAAAAAAAADw/wQAAAAAAAAAAADw/wUAAAAAAAAAAADw/wYAAAAAAAAAAADw/wcAAAAAAAAAAADw/wgAAAAAAAAAAADw/wkAAAAAAAAAAADw/woAAAAAAAAAAADw/wsAAAAAAAAAAADx//f/AAAAAAAAAADx//j/AAAAAAAAAADx//n/AAAAAAAAAADx//r/AAAAAAAAAADx//v/AAAAAAAAAADx//z/AAAAAAAAAADx//3/AAAAAAAAAADx//7/AAAAAAAAAADx////AAAAAAAAAADx/wAAAAAAAAAAAADx/wEAAAAAAAAAAADx/wIAAAAAAAAAAADx/wMAAAAAAAAAAADx/wQAAAAAAAAAAADx/wUAAAAAAAAAAADx/wYAAAAAAAAAAADx/wcAAAAAAAAAAADx/wgAAAAAAAAAAADx/wkAAAAAAAAAAADx/woAAAAAAAAAAADx/wsAAAAAAAAAAADy//f/AAAAAAAAAADy//j/AAAAAAAAAADy//n/AAAAAAAAAADy//r/AAAAAAAAAADy//v/AAAAAAEAAADy//z/AAAAAAEAAADy//3/AAAAAAEAAADy//7/AAAAAAEAAADy////AAAAAAEAAADy/wAAAAAAAAEAAADy/wEAAAAAAAAAAADy/wIAAAAAAAAAAADy/wMAAAAAAAAAAADy/wQAAAAAAAAAAADy/wUAAAAAAAAAAADy/wYAAAAAAAAAAADy/wcAAAAAAAAAAADy/wgAAAAAAAAAAADy/wkAAAAAAAAAAADy/woAAAAAAAAAAADy/wsAAAAAAAAAAADz//f/AAAAAAAAAADz//j/AAAAAAAAAADz//n/AAAAAAAAAADz//r/AAAAAAAAAADz//v/AAAAAAEAAADz//z/AAAAAAEAAADz//3/AAAAAAAAAADz//7/AAAAAAAAAADz////AAAAAAAAAADz/wAAAAAAAAEAAADz/wEAAAAAAAEAAADz/wIAAAAAAAEAAADz/wMAAAAAAAEAAADz/wQAAAAAAAAAAADz/wUAAAAAAAAAAADz/wYAAAAAAAAAAADz/wcAAAAAAAAAAADz/wgAAAAAAAAAAADz/wkAAAAAAAAAAADz/woAAAAAAAAAAADz/wsAAAAAAAAAAAD0//f/AAAAAAAAAAD0//j/AAAAAAAAAAD0//n/AAAAAAAAAAD0/wYAAAAAAAAAAAD0/wcAAAAAAAAAAAD0/wgAAAAAAAAAAAD0/wkAAAAAAAAAAAD0/woAAAAAAAAAAAD0/wsAAAAAAAAAAAD1//f/AAAAAAAAAAD1//j/AAAAAAAAAAD1//n/AAAAAAAAAAD1//r/AAAAAAAAAAD1//v/AAAAAAEAAAD1//z/AAAAAAEAAAD1//3/AAAAAAAAAAD1//7/AAAAAAAAAAD1////AAAAAAAAAAD1/wAAAAAAAAAAAAD1/wEAAAAAAAAAAAD1/wIAAAAAAAAAAAD1/wMAAAAAAAAAAAD1/wQAAAAAAAEAAAD1/wUAAAAAAAEAAAD1/wcAAAAAAAAAAAD1/wgAAAAAAAAAAAD1/wkAAAAAAAAAAAD1/woAAAAAAAAAAAD1/wsAAAAAAAAAAAD2//f/AAAAAAAAAAD2//j/AAAAAAAAAAD2//n/AAAAAAAAAAD2//r/AAAAAAAAAAD2//v/AAAAAAEAAAD2//z/AAAAAAEAAAD2//3/AAAAAAAAAAD2//7/AAAAAAAAAAD2////AAAAAAAAAAD2/wAAAAAAAAAAAAD2/wEAAAAAAAAAAAD2/wIAAAAAAAAAAAD2/wMAAAAAAAAAAAD2/wQAAAAAAAAAAAD2/wUAAAAAAAAAAAD2/wcAAAAAAAAAAAD2/wgAAAAAAAAAAAD2/wkAAAAAAAAAAAD2/woAAAAAAAAAAAD2/wsAAAAAAAAAAAD3//f/AAAAAAAAAAD3//j/AAAAAAAAAAD3//n/AAAAAAAAAAD3//r/AAAAAAAAAAD3//v/AAAAAAEAAAD3//z/AAAAAAEAAAD3//3/AAAAAAAAAAD3//7/AAAAAAAAAAD3////AAAAAAAAAAD3/wAAAAAAAAAAAAD3/wEAAAAAAAAAAAD3/wIAAAAAAAAAAAD3/wMAAAAAAAAAAAD3/wQAAAAAAAAAAAD3/wUAAAAAAAAAAAD3/wYAAAAAAAEAAAD3/wgAAAAAAAAAAAD3/wkAAAAAAAAAAAD3/woAAAAAAAAAAAD3/wsAAAAAAAAAAAD4//f/AAAAAAAAAAD4//j/AAAAAAAAAAD4//n/AAAAAAAAAAD4//r/AAAAAAAAAAD4//v/AAAAAAEAAAD4//z/AAAAAAEAAAD4//3/AAAAAAAAAAD4//7/AAAAAAAAAAD4////AAAAAAAAAAD4/wAAAAAAAAAAAAD4/wEAAAAAAAAAAAD4/wIAAAAAAAAAAAD4/wMAAAAAAAAAAAD4/wQAAAAAAAAAAAD4/wUAAAAAAAAAAAD4/wYAAAAAAAEAAAD4/wgAAAAAAAEAAAD4/wkAAAAAAAAAAAD4/woAAAAAAAAAAAD4/wsAAAAAAAAAAAD5//f/AAAAAAAAAAD5//j/AAAAAAAAAAD5//n/AAAAAAAAAAD5//r/AAAAAAAAAAD5//v/AAAAAAEAAAD5//z/AAAAAAEAAAD5//3/AAAAAAAAAAD5//7/AAAAAAAAAAD5////AAAAAAAAAAD5/wAAAAAAAAAAAAD5/wEAAAAAAAAAAAD5/wIAAAAAAAAAAAD5/wMAAAAAAAAAAAD5/wQAAAAAAAAAAAD5/wUAAAAAAAAAAAD5/wYAAAAAAAAAAAD5/wgAAAAAAAEAAAD5/wkAAAAAAAAAAAD5/woAAAAAAAAAAAD5/wsAAAAAAAAAAAD6//f/AAAAAAAAAAD6//j/AAAAAAAAAAD6//n/AAAAAAAAAAD6//r/AAAAAAAAAAD6//v/AAAAAAEAAAD6//z/AAAAAAEAAAD6//3/AAAAAAAAAAD6//7/AAAAAAAAAAD6////AAAAAAAAAAD6/wAAAAAAAAAAAAD6/wEAAAAAAAAAAAD6/wIAAAAAAAAAAAD6/wMAAAAAAAAAAAD6/wQAAAAAAAAAAAD6/wUAAAAAAAAAAAD6/wYAAAAAAAAAAAD6/wcAAAAAAAAAAAD6/wkAAAAAAAEAAAD6/woAAAAAAAAAAAD6/wsAAAAAAAAAAAD7//f/AAAAAAAAAAD7//j/AAAAAAAAAAD7//n/AAAAAAAAAAD7//r/AAAAAAAAAAD7//v/AAAAAAEAAAD7//z/AAAAAAEAAAD7//3/AAAAAAAAAAD7//7/AAAAAAAAAAD7////AAAAAAAAAAD7/wAAAAAAAAAAAAD7/wEAAAAAAAAAAAD7/wIAAAAAAAAAAAD7/wMAAAAAAAAAAAD7/wQAAAAAAAAAAAD7/wUAAAAAAAAAAAD7/wYAAAAAAAAAAAD7/wcAAAAAAAAAAAD7/wkAAAAAAAEAAAD7/woAAAAAAAAAAAD7/wsAAAAAAAAAAAD8//f/AAAAAAAAAAD8//j/AAAAAAAAAAD8//n/AAAAAAAAAAD8//r/AAAAAAAAAAD8//v/AAAAAAEAAAD8//z/AAAAAAEAAAD8//3/AAAAAAAAAAD8//7/AAAAAAAAAAD8////AAAAAAAAAAD8/wAAAAAAAAAAAAD8/wEAAAAAAAAAAAD8/wIAAAAAAAAAAAD8/wMAAAAAAAAAAAD8/wQAAAAAAAAAAAD8/wUAAAAAAAAAAAD8/wYAAAAAAAAAAAD8/wcAAAAAAAAAAAD8/wkAAAAAAAEAAAD8/woAAAAAAAAAAAD8/wsAAAAAAAAAAAD9//f/AAAAAAAAAAD9//j/AAAAAAAAAAD9//n/AAAAAAAAAAD9//r/AAAAAAAAAAD9////AAAAAAAAAAD9/wAAAAAAAAAAAAD9/wEAAAAAAAAAAAD9/wIAAAAAAAAAAAD9/wMAAAAAAAAAAAD9/wQAAAAAAAAAAAD9/wUAAAAAAAAAAAD9/wYAAAAAAAAAAAD9/wcAAAAAAAAAAAD9/wkAAAAAAAEAAAD9/woAAAAAAAAAAAD9/wsAAAAAAAAAAAD+//f/AAAAAAAAAAD+//j/AAAAAAAAAAD+//n/AAAAAAAAAAD+//z/AAAAAAAAAAD+//3/AAAAAAAAAAD+/wEAAAAAAAAAAAD+/wIAAAAAAAAAAAD+/wMAAAAAAAAAAAD+/wQAAAAAAAAAAAD+/wUAAAAAAAAAAAD+/wYAAAAAAAAAAAD+/wcAAAAAAAAAAAD+/woAAAAAAAEAAAD+/wsAAAAAAAAAAAD///f/AAAAAAAAAAD///j/AAAAAAAAAAD///n/AAAAAAAAAAD///v/AAAAAAAAAAD///z/AAAAAAAAAAD///3/AAAAAAAAAAD///7/AAAAAAAAAAD/////AAAAAAAAAAD//wUAAAAAAAEAAAD//wYAAAAAAAAAAAD//wcAAAAAAAAAAAD//wgAAAAAAAAAAAD//woAAAAAAAEAAAD//wsAAAAAAAAAAAAAAPf/AAAAAAAAAAAAAPj/AAAAAAAAAAAAAPn/AAAAAAAAAAAAAPv/AAAAAAAAAAAAAPz/AAAAAAAAAAAAAP3/AAAAAAAAAAAAAP7/AAAAAAAAAAAAAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAUAAAAAAAEAAAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAoAAAAAAAEAAAAAAAsAAAAAAAAAAAABAPf/AAAAAAAAAAABAPj/AAAAAAAAAAABAPn/AAAAAAAAAAABAPv/AAAAAAAAAAABAPz/AAAAAAAAAAABAP3/AAAAAAAAAAABAP7/AAAAAAAAAAABAP//AAAAAAAAAAABAAAAAAAAAAAAAAABAAEAAAAAAAAAAAABAAIAAAAAAAAAAAABAAUAAAAAAAEAAAABAAYAAAAAAAAAAAABAAcAAAAAAAAAAAABAAgAAAAAAAAAAAABAAoAAAAAAAEAAAABAAsAAAAAAAAAAAACAPf/AAAAAAAAAAACAPj/AAAAAAAAAAACAPn/AAAAAAAAAAACAPv/AAAAAAEAAAACAPz/AAAAAAEAAAACAP3/AAAAAAEAAAACAP7/AAAAAAEAAAACAP//AAAAAAAAAAACAAAAAAAAAAAAAAACAAEAAAAAAAAAAAACAAMAAAAAAAAAAAACAAQAAAAAAAAAAAACAAUAAAAAAAEAAAACAAYAAAAAAAAAAAACAAcAAAAAAAAAAAACAAgAAAAAAAAAAAACAAoAAAAAAAEAAAACAAsAAAAAAAAAAAADAPf/AAAAAAAAAAADAPj/AAAAAAAAAAADAPn/AAAAAAAAAAADAPz/AAAAAAAAAAADAP3/AAAAAAAAAAADAP7/AAAAAAEAAAADAP//AAAAAAEAAAADAAAAAAAAAAAAAAADAAEAAAAAAAAAAAADAAIAAAAAAAAAAAADAAMAAAAAAAAAAAADAAQAAAAAAAEAAAADAAUAAAAAAAEAAAADAAYAAAAAAAAAAAADAAcAAAAAAAAAAAADAAgAAAAAAAAAAAADAAoAAAAAAAEAAAADAAsAAAAAAAAAAAAEAPf/AAAAAAAAAAAEAPj/AAAAAAAAAAAEAPn/AAAAAAAAAAAEAPr/AAAAAAAAAAAEAPz/AAAAAAAAAAAEAP3/AAAAAAAAAAAEAP7/AAAAAAAAAAAEAP//AAAAAAEAAAAEAAAAAAAAAAEAAAAEAAEAAAAAAAEAAAAEAAIAAAAAAAEAAAAEAAMAAAAAAAEAAAAEAAQAAAAAAAEAAAAEAAUAAAAAAAEAAAAEAAYAAAAAAAAAAAAEAAcAAAAAAAAAAAAEAAgAAAAAAAAAAAAEAAoAAAAAAAEAAAAEAAsAAAAAAAAAAAAFAPf/AAAAAAAAAAAFAPj/AAAAAAAAAAAFAPn/AAAAAAAAAAAFAPr/AAAAAAAAAAAFAPz/AAAAAAEAAAAFAP3/AAAAAAAAAAAFAP7/AAAAAAAAAAAFAP//AAAAAAAAAAAFAAAAAAAAAAEAAAAFAAEAAAAAAAEAAAAFAAIAAAAAAAAAAAAFAAMAAAAAAAAAAAAFAAQAAAAAAAAAAAAFAAUAAAAAAAAAAAAFAAYAAAAAAAAAAAAFAAcAAAAAAAAAAAAFAAgAAAAAAAAAAAAFAAoAAAAAAAEAAAAFAAsAAAAAAAAAAAAGAPf/AAAAAAAAAAAGAPj/AAAAAAAAAAAGAPn/AAAAAAAAAAAGAPr/AAAAAAAAAAAGAPz/AAAAAAEAAAAGAP3/AAAAAAAAAAAGAP7/AAAAAAAAAAAGAP//AAAAAAAAAAAGAAAAAAAAAAAAAAAGAAEAAAAAAAAAAAAGAAIAAAAAAAAAAAAGAAMAAAAAAAAAAAAGAAQAAAAAAAAAAAAGAAUAAAAAAAAAAAAGAAYAAAAAAAAAAAAGAAcAAAAAAAAAAAAGAAgAAAAAAAEAAAAGAAoAAAAAAAAAAAAGAAsAAAAAAAAAAAAHAPf/AAAAAAAAAAAHAPj/AAAAAAAAAAAHAPn/AAAAAAAAAAAHAPr/AAAAAAAAAAAHAP3/AAAAAAAAAAAHAP7/AAAAAAAAAAAHAP//AAAAAAAAAAAHAAAAAAAAAAAAAAAHAAEAAAAAAAAAAAAHAAIAAAAAAAAAAAAHAAMAAAAAAAAAAAAHAAQAAAAAAAAAAAAHAAUAAAAAAAAAAAAHAAYAAAAAAAAAAAAHAAcAAAAAAAEAAAAHAAgAAAAAAAEAAAAHAAoAAAAAAAAAAAAHAAsAAAAAAAAAAAAIAPf/AAAAAAAAAAAIAPj/AAAAAAAAAAAIAPn/AAAAAAAAAAAIAPr/AAAAAAAAAAAIAPv/AAAAAAAAAAAIAP3/AAAAAAEAAAAIAP7/AAAAAAEAAAAIAP//AAAAAAAAAAAIAAAAAAAAAAAAAAAIAAEAAAAAAAAAAAAIAAIAAAAAAAAAAAAIAAMAAAAAAAAAAAAIAAQAAAAAAAAAAAAIAAUAAAAAAAAAAAAIAAYAAAAAAAEAAAAIAAcAAAAAAAEAAAAIAAgAAAAAAAAAAAAIAAoAAAAAAAAAAAAIAAsAAAAAAAAAAAAJAPf/AAAAAAAAAAAJAPj/AAAAAAAAAAAJAPn/AAAAAAAAAAAJAPr/AAAAAAAAAAAJAPv/AAAAAAAAAAAJAPz/AAAAAAAAAAAJAP7/AAAAAAEAAAAJAP//AAAAAAEAAAAJAAAAAAAAAAEAAAAJAAEAAAAAAAEAAAAJAAIAAAAAAAEAAAAJAAMAAAAAAAEAAAAJAAQAAAAAAAEAAAAJAAUAAAAAAAEAAAAJAAYAAAAAAAEAAAAJAAcAAAAAAAAAAAAJAAgAAAAAAAAAAAAJAAoAAAAAAAAAAAAJAAsAAAAAAAAAAAAKAPf/AAAAAAAAAAAKAPj/AAAAAAAAAAAKAPn/AAAAAAAAAAAKAPr/AAAAAAAAAAAKAPv/AAAAAAAAAAAKAPz/AAAAAAAAAAAKAP//AAAAAAAAAAAKAAAAAAAAAAAAAAAKAAEAAAAAAAAAAAAKAAIAAAAAAAAAAAAKAAMAAAAAAAAAAAAKAAQAAAAAAAAAAAAKAAUAAAAAAAAAAAAKAAYAAAAAAAAAAAAKAAcAAAAAAAAAAAAKAAgAAAAAAAAAAAAKAAoAAAAAAAAAAAAKAAsAAAAAAAAAAAALAPf/AAAAAAAAAAALAPj/AAAAAAAAAAALAPn/AAAAAAAAAAALAPr/AAAAAAAAAAALAPv/AAAAAAAAAAALAPz/AAAAAAAAAAALAP3/AAAAAAAAAAALAAAAAAAAAAAAAAALAAEAAAAAAAAAAAALAAIAAAAAAAAAAAALAAMAAAAAAAAAAAALAAQAAAAAAAAAAAALAAUAAAAAAAAAAAALAAYAAAAAAAAAAAALAAcAAAAAAAAAAAALAAgAAAAAAAAAAAALAAoAAAAAAAAAAAALAAsAAAAAAAAAAAAMAPf/AAAAAAAAAAAMAPj/AAAAAAAAAAAMAPn/AAAAAAAAAAAMAPr/AAAAAAAAAAAMAPv/AAAAAAAAAAAMAPz/AAAAAAAAAAAMAP3/AAAAAAAAAAAMAP7/AAAAAAAAAAAMAAQAAAAAAAAAAAAMAAUAAAAAAAAAAAAMAAYAAAAAAAAAAAAMAAcAAAAAAAAAAAAMAAoAAAAAAAAAAAAMAAsAAAAAAAAAAAANAPf/AAAAAAAAAAANAPj/AAAAAAAAAAANAPn/AAAAAAAAAAANAPr/AAAAAAAAAAANAPv/AAAAAAAAAAANAPz/AAAAAAAAAAANAP3/AAAAAAAAAAANAP7/AAAAAAAAAAANAP//AAAAAAAAAAANAAAAAAAAAAAAAAANAAEAAAAAAAAAAAANAAIAAAAAAAAAAAANAAkAAAAAAAAAAAANAAoAAAAAAAAAAAANAAsAAAAAAAAAAAAOAPf/AAAAAAAAAAAOAPj/AAAAAAAAAAAOAPn/AAAAAAAAAAAOAPr/AAAAAAAAAAAOAPv/AAAAAAAAAAAOAPz/AAAAAAEAAAAOAP3/AAAAAAAAAAAOAP7/AAAAAAAAAAAOAP//AAAAAAAAAAAOAAAAAAAAAAAAAAAOAAEAAAAAAAAAAAAOAAIAAAAAAAAAAAAOAAMAAAAAAAAAAAAOAAQAAAAAAAAAAAAOAAUAAAAAAAAAAAAOAAYAAAAAAAAAAAAOAAcAAAAAAAAAAAAOAAgAAAAAAAAAAAAOAAkAAAAAAAAAAAAOAAoAAAAAAAAAAAAOAAsAAAAAAAAAAAAPAPf/AAAAAAAAAAAPAPj/AAAAAAAAAAAPAPn/AAAAAAEAAAAPAPr/AAAAAAEAAAAPAPv/AAAAAAEAAAAPAPz/AAAAAAEAAAAPAP3/AAAAAAEAAAAPAP7/AAAAAAEAAAAPAP//AAAAAAAAAAAPAAAAAAAAAAAAAAAPAAEAAAAAAAAAAAAPAAIAAAAAAAAAAAAPAAMAAAAAAAAAAAAPAAQAAAAAAAAAAAAPAAUAAAAAAAAAAAAPAAYAAAAAAAAAAAAPAAcAAAAAAAAAAAAPAAgAAAAAAAAAAAAPAAkAAAAAAAAAAAAPAAoAAAAAAAAAAAAPAAsAAAAAAAAAAAAQAPf/AAAAAAAAAAAQAPj/AAAAAAAAAAAQAPn/AAAAAAEAAAAQAPr/AAAAAAAAAAAQAPv/AAAAAAAAAAAQAPz/AAAAAAAAAAAQAP3/AAAAAAAAAAAQAP7/AAAAAAEAAAAQAP//AAAAAAAAAAAQAAAAAAAAAAAAAAAQAAEAAAAAAAAAAAAQAAIAAAAAAAAAAAAQAAMAAAAAAAAAAAAQAAQAAAAAAAAAAAAQAAUAAAAAAAAAAAAQAAYAAAAAAAAAAAAQAAcAAAAAAAAAAAAQAAgAAAAAAAAAAAAQAAkAAAAAAAAAAAAQAAoAAAAAAAAAAAAQAAsAAAAAAAAAAAARAPf/AAAAAAAAAAARAPj/AAAAAAAAAAARAPn/AAAAAAEAAAARAPr/AAAAAAAAAAARAPv/AAAAAAAAAAARAPz/AAAAAAAAAAARAP3/AAAAAAAAAAARAP7/AAAAAAEAAAARAP//AAAAAAAAAAARAAAAAAAAAAAAAAARAAEAAAAAAAAAAAARAAIAAAAAAAAAAAARAAMAAAAAAAAAAAARAAQAAAAAAAAAAAARAAUAAAAAAAAAAAARAAYAAAAAAAAAAAARAAcAAAAAAAAAAAARAAgAAAAAAAAAAAARAAkAAAAAAAAAAAARAAoAAAAAAAAAAAARAAsAAAAAAAAAAAASAPf/AAAAAAAAAAASAPj/AAAAAAAAAAASAPn/AAAAAAEAAAASAPr/AAAAAAEAAAASAPv/AAAAAAEAAAASAPz/AAAAAAAAAAASAP3/AAAAAAEAAAASAP7/AAAAAAEAAAASAP//AAAAAAAAAAASAAAAAAAAAAAAAAASAAEAAAAAAAAAAAASAAIAAAAAAAAAAAASAAMAAAAAAAAAAAASAAQAAAAAAAAAAAASAAUAAAAAAAAAAAASAAYAAAAAAAAAAAASAAcAAAAAAAAAAAASAAgAAAAAAAAAAAASAAkAAAAAAAAAAAASAAoAAAAAAAAAAAASAAsAAAAAAAAAAAATAPf/AAAAAAAAAAATAPj/AAAAAAAAAAATAPn/AAAAAAEAAAATAPr/AAAAAAEAAAATAPv/AAAAAAEAAAATAPz/AAAAAAEAAAATAP3/AAAAAAEAAAATAP7/AAAAAAEAAAATAP//AAAAAAAAAAATAAAAAAAAAAAAAAATAAEAAAAAAAAAAAATAAIAAAAAAAAAAAATAAMAAAAAAAAAAAATAAQAAAAAAAAAAAATAAUAAAAAAAAAAAATAAYAAAAAAAAAAAATAAcAAAAAAAAAAAATAAgAAAAAAAAAAAATAAkAAAAAAAAAAAATAAoAAAAAAAAAAAATAAsAAAAAAAAAAAAUAPf/AAAAAAAAAAAUAPj/AAAAAAAAAAAUAPn/AAAAAAAAAAAUAPr/AAAAAAAAAAAUAPv/AAAAAAAAAAAUAPz/AAAAAAAAAAAUAP3/AAAAAAAAAAAUAP7/AAAAAAAAAAAUAP//AAAAAAAAAAAUAAAAAAAAAAAAAAAUAAEAAAAAAAAAAAAUAAIAAAAAAAAAAAAUAAMAAAAAAAAAAAAUAAQAAAAAAAAAAAAUAAUAAAAAAAAAAAAUAAYAAAAAAAAAAAAUAAcAAAAAAAAAAAAUAAgAAAAAAAAAAAAUAAkAAAAAAAAAAAAUAAoAAAAAAAAAAAAUAAsAAAAAAAAAAAAVAPf/AAAAAAAAAAAVAPj/AAAAAAAAAAAVAPn/AAAAAAAAAAAVAPr/AAAAAAAAAAAVAPv/AAAAAAAAAAAVAPz/AAAAAAAAAAAVAP3/AAAAAAAAAAAVAP7/AAAAAAAAAAAVAP//AAAAAAAAAAAVAAAAAAAAAAAAAAAVAAEAAAAAAAAAAAAVAAIAAAAAAAAAAAAVAAMAAAAAAAAAAAAVAAQAAAAAAAAAAAAVAAUAAAAAAAAAAAAVAAYAAAAAAAAAAAAVAAcAAAAAAAAAAAAVAAgAAAAAAAAAAAAVAAkAAAAAAAAAAAAVAAoAAAAAAAAAAAAVAAsAAAAAAAAAAAA=") +tile_set = SubResource("TileSet_jmwcd") + +[node name="back" type="TileMapLayer" parent="Node2D"] + +[node name="build" type="TileMapLayer" parent="Node2D"] + +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="Control" type="Control" parent="CanvasLayer"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="mouse_finder" type="Area2D" parent="."] +unique_name_in_owner = true + +[node name="CollisionShape2D" type="CollisionShape2D" parent="mouse_finder"] +shape = SubResource("CircleShape2D_shajf") + +[node name="CB_add_pos" type="Node2D" parent="."] +unique_name_in_owner = true +y_sort_enabled = true + +[node name="CharacterBody2D" parent="CB_add_pos" instance=ExtResource("2_nvm7o")] +unit_id = "player" +unit_type = "player" + +[node name="other_character" parent="CB_add_pos" instance=ExtResource("4_eyxcn")] +position = Vector2(1048, 119) +unit_id = "test_1_1" +unit_type = "test_1" + +[node name="other_character2" parent="CB_add_pos" instance=ExtResource("4_eyxcn")] +position = Vector2(548, -436) +unit_id = "test_1_2" +unit_type = "test_1" + +[node name="other_character5" parent="CB_add_pos" instance=ExtResource("4_eyxcn")] +position = Vector2(-871, 442) +unit_id = "test_1_3" +unit_type = "test_1" + +[node name="other_character3" parent="CB_add_pos" instance=ExtResource("4_eyxcn")] +position = Vector2(-649, 649) +unit_id = "test_2_1" +unit_type = "test_2" + +[node name="other_character4" parent="CB_add_pos" instance=ExtResource("4_eyxcn")] +position = Vector2(-429, -13) +unit_id = "test_2_2" +unit_type = "test_2" + +[node name="food" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-917, -398) + +[node name="food2" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-986, -147) + +[node name="food3" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-491, -411) + +[node name="food4" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-66, -408) + +[node name="food5" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(343, -416) + +[node name="food6" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(433, 275) + +[node name="food7" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(161, 131) + +[node name="food8" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-176, 192) + +[node name="food9" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-580, 86) + +[node name="food10" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(-360, 343) + +[node name="food11" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(154, 522) + +[node name="food12" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(615, 638) + +[node name="food13" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(1091, 597) + +[node name="food14" parent="CB_add_pos" instance=ExtResource("3_fgsvv")] +position = Vector2(464, 0) + +[connection signal="gui_input" from="CanvasLayer/Control" to="." method="_on_control_gui_input"] diff --git a/tool/drawer_container/drawer_container.gd b/tool/drawer_container/drawer_container.gd new file mode 100644 index 0000000..99609d9 --- /dev/null +++ b/tool/drawer_container/drawer_container.gd @@ -0,0 +1,246 @@ +@tool +extends Container +##抽屉容器节点,可以用于将子节点侧滑,开启clip content可以做到完全隐藏子节点 +class_name DrawerContainer +##按钮大小 +@export var custom_button_size:Vector2=Vector2.ZERO: + set(val): + custom_button_size=val + side_button_size_changed() + fit_children() + fit_side_button() +var button_size:Vector2=Vector2(100,200): + set(val): + button_size=val + fit_children() + fit_side_button() +##按钮场景,根节点必须为basebutton或继承自basebutton,留空则使用默认按钮 +@export var button_tscn:PackedScene +##布局模式 +@export var button_model:BUTTON_MODEL=BUTTON_MODEL.TOP +enum BUTTON_MODEL{ + ##按钮在上方,向下收纳 + TOP=0, + ##按钮在下方,向上收纳 + END=1, + ##按钮在左方,向右收纳 + LEFT=2, + ##按钮在右方,向左收纳 + RIGHT=3, +} +##动画时间 +@export var animation_time:float=0.5 +##是否自动隐藏 +@export var is_auto_hide:bool=false +#隐藏计时器 +@onready var hide_timer:Timer=Timer.new() +##是否自动收缩 +@export var is_auto_shrink:bool=false +##是否禁用按钮 +@export var disable_button:bool=false: + set(val): + disable_button=val + fit_side_button() + fit_side_button() + if side_button!=null: + if val : + side_button.hide() + else: + side_button.show() +##动画过渡模式 +@export var animation_model:Tween.TransitionType=Tween.TransitionType.TRANS_BACK +##当前持有的动画 +var now_keep_tween:Tween +##当前是否打开 +@export var is_open:bool=true +##持有的抽屉控制按钮的实例(内部节点) +var side_button:BaseButton + +##获取当前持有的侧边按钮 +func get_side_button()->BaseButton: + return side_button + +##当前动画状态,0为完全打开,1为完全关闭 +@export var rag:float=0: + set(val): + rag=val + fit_side_button() + fit_children() + minimum_size_changed.emit() +func _ready() -> void: + add_child(hide_timer,false,Node.INTERNAL_MODE_FRONT) + hide_timer.one_shot=true + hide_timer.timeout.connect(hide_time_out) + if is_open: + rag=0 + else: + rag=1 + if button_tscn!=null: + var new_btn=button_tscn.instantiate() + if new_btn is BaseButton: + side_button=new_btn + add_child(new_btn,false,Node.INTERNAL_MODE_FRONT) + else: + new_btn=Button.new() + side_button=new_btn + add_child(new_btn,false,Node.INTERNAL_MODE_FRONT) + else: + var new_btn=Button.new() + side_button=new_btn + add_child(new_btn,false,Node.INTERNAL_MODE_FRONT) + side_button.pressed.connect(change_open) + side_button.minimum_size_changed.connect(side_button_size_changed) + side_button_size_changed() + if disable_button: + side_button.hide() + resized.connect(resize) + resize() + _re_ready() + pass # Replace with function body. +##因为占用了ready函数,所以留了一个新的虚函数给使用节点重写 +func _re_ready()->void: + + + + pass +##对侧边栏按钮进行适配 +func fit_side_button()->void: + var rect:Rect2 + if side_button!=null: + match button_model: + BUTTON_MODEL.TOP: + rect=Rect2(0,(size.y-button_size.y)*rag,size.x,button_size.y) + BUTTON_MODEL.END: + rect=Rect2(0,(size.y-button_size.y)*(1-rag),size.x,button_size.y) + BUTTON_MODEL.LEFT: + rect=Rect2((size.x-button_size.x)*rag,0,button_size.x,size.y) + BUTTON_MODEL.RIGHT: + rect=Rect2((size.x-button_size.x)*(1-rag),0,button_size.x,size.y) + + _: + rect=Rect2() + fit_child_in_rect(side_button,rect) +##对子节点进行适配 +func fit_children()->void: + var rect:Rect2 + if !disable_button: + match button_model: + BUTTON_MODEL.TOP: + rect=Rect2(0,(size.y-button_size.y)*rag+button_size.y,size.x,size.y-button_size.y) + BUTTON_MODEL.END: + rect=Rect2(0,(size.y-button_size.y)*(-rag),size.x,size.y-button_size.y) + BUTTON_MODEL.LEFT: + rect=Rect2((size.x-button_size.x)*rag+button_size.x,0,size.x-button_size.x,size.y) + BUTTON_MODEL.RIGHT: + rect=Rect2((size.x-button_size.x)*(-rag),0,size.x-button_size.x,size.y) + _: + rect=Rect2() + else: + match button_model: + BUTTON_MODEL.TOP: + rect=Rect2(0,(size.y)*rag+button_size.y,size.x,size.y) + BUTTON_MODEL.END: + rect=Rect2(0,(size.y)*(-rag),size.x,size.y) + BUTTON_MODEL.LEFT: + rect=Rect2((size.x)*rag,0,size.x,size.y) + BUTTON_MODEL.RIGHT: + rect=Rect2((size.x)*(-rag),0,size.x,size.y) + _: + rect=Rect2() + + + pass + for i in get_children(): + if i is Control: + fit_child_in_rect(i,rect) + +##更改窗口的开闭状态,使用动画 +func change_open(opened:bool=false): + if opened==is_open: + return + if now_keep_tween!=null: + now_keep_tween.kill() + if !opened: + if is_auto_hide: + hide_timer.start(animation_time) + pass + now_keep_tween=create_tween() + now_keep_tween.set_trans(animation_model) + now_keep_tween.tween_property(self,"rag",1,animation_time) + else: + now_keep_tween=create_tween() + now_keep_tween.set_trans(animation_model) + now_keep_tween.tween_property(self,"rag",0,animation_time) + if is_auto_hide: + show() + hide_timer.stop() + is_open=opened + + + + pass +##当此节点大小改变时触发 +func resize(): + fit_side_button() + fit_children() +##重写的虚函数,用于获取当前节点的最小大小 +func _get_minimum_size() -> Vector2: + var res:Vector2=Vector2.ZERO + + for i in get_children(): + if i is Control: + var i_size:Vector2=i.get_minimum_size() + res=Vector2(max(res.x,i_size.x),max(res.y,i_size.y)) + if not is_auto_shrink: + if !disable_button: + match button_model: + BUTTON_MODEL.TOP: + res=Vector2(max(res.x,button_size.x),res.y+button_size.y) + BUTTON_MODEL.END: + res=Vector2(max(res.x,button_size.x),res.y+button_size.y) + BUTTON_MODEL.LEFT: + res=Vector2(res.x+button_size.x,max(res.y,button_size.y)) + BUTTON_MODEL.RIGHT: + res=Vector2(res.x+button_size.x,max(res.y,button_size.y)) + _: + res=Vector2.ZERO + else: + if !disable_button: + match button_model: + BUTTON_MODEL.TOP: + res=Vector2(max(res.x,button_size.x),res.y*rag+button_size.y) + BUTTON_MODEL.END: + res=Vector2(max(res.x,button_size.x),res.y*rag+button_size.y) + BUTTON_MODEL.LEFT: + res=Vector2(res.x*rag+button_size.x,max(res.y,button_size.y)) + BUTTON_MODEL.RIGHT: + res=Vector2(res.x*rag+button_size.x,max(res.y,button_size.y)) + _: + res=Vector2.ZERO + else: + match button_model: + BUTTON_MODEL.TOP: + res=Vector2(res.x,res.y*(1-rag)) + BUTTON_MODEL.END: + res=Vector2(res.x*rag,res.y*(1-rag)) + BUTTON_MODEL.LEFT: + res=Vector2(res.x*(1-rag),res.y) + BUTTON_MODEL.RIGHT: + res=Vector2(res.x*rag,res.y*(1-rag)) + _: + res=Vector2.ZERO + pass + + return res +##当侧边栏按钮大小更改时调用 +func side_button_size_changed()->void: + if side_button: + var new_size=side_button.get_minimum_size() + button_size=Vector2(max(custom_button_size.x,new_size.x),max(custom_button_size.y,new_size.y)) + +func _process(delta: float) -> void: + if Engine.is_editor_hint(): + fit_children() + pass +func hide_time_out(): + hide() diff --git a/tool/state_machine/state.gd b/tool/state_machine/state.gd new file mode 100644 index 0000000..517612b --- /dev/null +++ b/tool/state_machine/state.gd @@ -0,0 +1,45 @@ +extends Node +##状态机根节点类 +class_name StateMachine +##状态机进入时候的状态 +@export var init_state:NodePath="stable_1"##进入时的状态 +@export var owner_player:NodePath="stable_1"##拥有此状态机的节点路径 +@onready var state_list=get_children() ##所拥有的状态节点的数组 +@onready var state_now:State=get_node(init_state) ##当前的状态节点 +@onready var player =get_node(owner_player) ##通过角色路径获取到的节点 +func _ready(): + set_process(false) +##启动状态机 +func launch(): + state_now=get_node(init_state) + state_now.enter_state(1) + set_process(true) +func _process(delta): + if state_now!=null: + state_now.update_state(delta) +func _physics_process(delta): + if state_now!=null: + state_now.update_state_phy(delta) +##判断n节点是否在状态机节点所拥有的状态中 +func is_in_list(n)->bool: + if n in state_list: + return true + return false +##将状态节点切换为n,并调用对应的init stable方法和exit stable方法,传入s +func change_state(n:String,s): + var change_to=get_node(n) + if change_to&&is_in_list(change_to)&&state_now!=change_to: + state_now.exit_state() + state_now=change_to + state_now.enter_state(s) + +##获取当前状态的状态名 +func get_state_name()->String: + return self.state_now.state_name + +func get_state(state_name:String): + return self.get_node(state_name) +func send_message(type:String,n=0): + if state_now!=null: + state_now.process_message(type,n) + pass diff --git a/tool/state_machine/state_root.gd b/tool/state_machine/state_root.gd new file mode 100644 index 0000000..5a5ffde --- /dev/null +++ b/tool/state_machine/state_root.gd @@ -0,0 +1,32 @@ +extends Node +class_name State +var timer +@onready var root=get_node("..") as StateMachine +@export var need_timer:bool=false +func _ready(): + if need_timer: + timer=Timer.new() + timer.set_one_shot(true) + self.add_child(timer) + timer.timeout.connect(Callable(self,"time_out")) + +func enter_state(n): + pass +func update_state(delta): + pass +func update_state_phy(delta): + pass +func exit_state(): + pass +func change_to_state(state_name:String,s=0): + root.change_state(state_name,s) + pass + +func time_out(): + pass +func get_player(): + return root.player +func process_message(type:String,n): + + + pass