diff --git a/bt/actions/bt_await_animation.cpp b/bt/actions/bt_await_animation.cpp new file mode 100644 index 0000000..67873db --- /dev/null +++ b/bt/actions/bt_await_animation.cpp @@ -0,0 +1,104 @@ +/** + * bt_await_animation.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "bt_await_animation.h" + +//**** Setters / Getters + +void BTAwaitAnimation::set_animation_player(Ref p_animation_player) { + animation_player_param = p_animation_player; + emit_changed(); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { + animation_player_param->connect(SNAME("changed"), Callable(this, SNAME("emit_changed"))); + } +} + +void BTAwaitAnimation::set_animation_name(StringName p_animation_name) { + animation_name = p_animation_name; + emit_changed(); +} + +void BTAwaitAnimation::set_max_time(double p_max_time) { + max_time = p_max_time; + emit_changed(); +} + +//**** Task Implementation + +String BTAwaitAnimation::get_configuration_warning() const { + String warning = BTAction::get_configuration_warning(); + if (!warning.is_empty()) { + warning += "\n"; + } + + if (animation_player_param.is_null()) { + warning += "Animation Player parameter is not set.\n"; + } else { + if (animation_player_param->get_value_source() == BBParam::SAVED_VALUE && animation_player_param->get_saved_value().is_zero()) { + warning += "Path to AnimationPlayer node is not set.\n"; + } else if (animation_player_param->get_value_source() == BBParam::BLACKBOARD_VAR && animation_player_param->get_variable().is_empty()) { + warning += "AnimationPlayer blackboard variable is not set.\n"; + } + } + if (animation_name == StringName()) { + warning += "Animation Name is required in order to wait for the animation to finish.\n"; + } + if (max_time <= 0.0) { + warning += "Max time should be greater than 0.0.\n"; + } + + return warning; +} + +String BTAwaitAnimation::_generate_name() const { + return "AwaitAnimation" + + (animation_name != StringName() ? vformat(" \"%s\"", animation_name) : " ???") + + vformat(" max_time: %ss", Math::snapped(max_time, 0.001)); +} + +void BTAwaitAnimation::_setup() { + setup_failed = true; + ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTAwaitAnimation: AnimationPlayer parameter is not set."); + animation_player = Object::cast_to(animation_player_param->get_value(get_agent(), get_blackboard())); + ERR_FAIL_COND_MSG(animation_player == nullptr, "BTAwaitAnimation: Failed to get AnimationPlayer."); + ERR_FAIL_COND_MSG(animation_name == StringName(), "BTAwaitAnimation: Animation Name is not set."); + ERR_FAIL_COND_MSG(!animation_player->has_animation(animation_name), vformat("BTAwaitAnimation: Animation not found: %s", animation_name)); + setup_failed = false; +} + +int BTAwaitAnimation::_tick(double p_delta) { + ERR_FAIL_COND_V_MSG(setup_failed == true, FAILURE, "BTAwaitAnimation: _setup() failed - returning FAILURE."); + + // ! Doing this check instead of using signal due to a bug in Godot: https://github.com/godotengine/godot/issues/76127 + if (animation_player->is_playing() && animation_player->get_assigned_animation() == animation_name) { + if (get_elapsed_time() < max_time) { + return RUNNING; + } else if (max_time > 0.0) { + WARN_PRINT(vformat("BTAwaitAnimation: Waiting time for the \"%s\" animation exceeded the allocated %s sec.", animation_name, max_time)); + } + } + return SUCCESS; +} + +//**** Godot + +void BTAwaitAnimation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_animation_player", "p_anim_player"), &BTAwaitAnimation::set_animation_player); + ClassDB::bind_method(D_METHOD("get_animation_player"), &BTAwaitAnimation::get_animation_player); + ClassDB::bind_method(D_METHOD("set_animation_name", "p_anim_name"), &BTAwaitAnimation::set_animation_name); + ClassDB::bind_method(D_METHOD("get_animation_name"), &BTAwaitAnimation::get_animation_name); + ClassDB::bind_method(D_METHOD("set_max_time", "p_time_sec"), &BTAwaitAnimation::set_max_time); + ClassDB::bind_method(D_METHOD("get_max_time"), &BTAwaitAnimation::get_max_time); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "animation_player", PROPERTY_HINT_RESOURCE_TYPE, "BBNode"), "set_animation_player", "get_animation_player"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation_name"), "set_animation_name", "get_animation_name"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_time", PROPERTY_HINT_RANGE, "0.0,100.0"), "set_max_time", "get_max_time"); +} diff --git a/bt/actions/bt_await_animation.h b/bt/actions/bt_await_animation.h new file mode 100644 index 0000000..7eb34eb --- /dev/null +++ b/bt/actions/bt_await_animation.h @@ -0,0 +1,52 @@ +/** + * bt_await_animation.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef BT_AWAIT_ANIMATION_H +#define BT_AWAIT_ANIMATION_H + +#include "bt_action.h" + +#include "modules/limboai/blackboard/bb_param/bb_node.h" + +#include "scene/animation/animation_player.h" + +class BTAwaitAnimation : public BTAction { + GDCLASS(BTAwaitAnimation, BTAction); + +private: + Ref animation_player_param; + StringName animation_name; + double max_time = 1.0; + + AnimationPlayer *animation_player = nullptr; + bool setup_failed = false; + +protected: + static void _bind_methods(); + + virtual String _generate_name() const override; + virtual void _setup() override; + virtual int _tick(double p_delta) override; + +public: + void set_animation_player(Ref p_animation_player); + Ref get_animation_player() const { return animation_player_param; } + + void set_animation_name(StringName p_animation_name); + StringName get_animation_name() const { return animation_name; } + + void set_max_time(double p_max_time); + double get_max_time() const { return max_time; } + + virtual String get_configuration_warning() const override; +}; + +#endif // BT_AWAIT_ANIMATION \ No newline at end of file diff --git a/bt/actions/bt_pause_animation.cpp b/bt/actions/bt_pause_animation.cpp new file mode 100644 index 0000000..ee1b142 --- /dev/null +++ b/bt/actions/bt_pause_animation.cpp @@ -0,0 +1,70 @@ +/** + * bt_pause_animation.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "bt_pause_animation.h" + +//**** Setters / Getters + +void BTPauseAnimation::set_animation_player(Ref p_animation_player) { + animation_player_param = p_animation_player; + emit_changed(); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { + animation_player_param->connect(SNAME("changed"), Callable(this, SNAME("emit_changed"))); + } +} + +//**** Task Implementation + +String BTPauseAnimation::get_configuration_warning() const { + String warning = BTAction::get_configuration_warning(); + if (!warning.is_empty()) { + warning += "\n"; + } + + if (animation_player_param.is_null()) { + warning += "Animation Player parameter is not set.\n"; + } else { + if (animation_player_param->get_value_source() == BBParam::SAVED_VALUE && animation_player_param->get_saved_value().is_zero()) { + warning += "Path to AnimationPlayer node is not set.\n"; + } else if (animation_player_param->get_value_source() == BBParam::BLACKBOARD_VAR && animation_player_param->get_variable().is_empty()) { + warning += "AnimationPlayer blackboard variable is not set.\n"; + } + } + + return warning; +} + +String BTPauseAnimation::_generate_name() const { + return "PauseAnimation"; +} + +void BTPauseAnimation::_setup() { + setup_failed = true; + ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTPauseAnimation: AnimationPlayer parameter is not set."); + animation_player = Object::cast_to(animation_player_param->get_value(get_agent(), get_blackboard())); + ERR_FAIL_COND_MSG(animation_player == nullptr, "BTPauseAnimation: Failed to get AnimationPlayer."); + setup_failed = false; +} + +int BTPauseAnimation::_tick(double p_delta) { + ERR_FAIL_COND_V_MSG(setup_failed == true, FAILURE, "BTPauseAnimation: _setup() failed - returning FAILURE."); + animation_player->pause(); + return SUCCESS; +} + +//**** Godot + +void BTPauseAnimation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_animation_player", "p_anim_player"), &BTPauseAnimation::set_animation_player); + ClassDB::bind_method(D_METHOD("get_animation_player"), &BTPauseAnimation::get_animation_player); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "animation_player", PROPERTY_HINT_RESOURCE_TYPE, "BBNode"), "set_animation_player", "get_animation_player"); +} diff --git a/bt/actions/bt_pause_animation.h b/bt/actions/bt_pause_animation.h new file mode 100644 index 0000000..a54a436 --- /dev/null +++ b/bt/actions/bt_pause_animation.h @@ -0,0 +1,44 @@ +/** + * bt_pause_animation.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef BT_PAUSE_ANIMATION_H +#define BT_PAUSE_ANIMATION_H + +#include "bt_action.h" + +#include "modules/limboai/blackboard/bb_param/bb_node.h" + +#include "scene/animation/animation_player.h" + +class BTPauseAnimation : public BTAction { + GDCLASS(BTPauseAnimation, BTAction); + +private: + Ref animation_player_param; + + AnimationPlayer *animation_player = nullptr; + bool setup_failed = false; + +protected: + static void _bind_methods(); + + virtual String _generate_name() const override; + virtual void _setup() override; + virtual int _tick(double p_delta) override; + +public: + void set_animation_player(Ref p_animation_player); + Ref get_animation_player() const { return animation_player_param; } + + virtual String get_configuration_warning() const override; +}; + +#endif // BT_PAUSE_ANIMATION diff --git a/bt/actions/bt_play_animation.cpp b/bt/actions/bt_play_animation.cpp new file mode 100644 index 0000000..c37e0d1 --- /dev/null +++ b/bt/actions/bt_play_animation.cpp @@ -0,0 +1,138 @@ +/** + * bt_play_animation.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "bt_play_animation.h" + +#include "core/math/math_funcs.h" + +//**** Setters / Getters + +void BTPlayAnimation::set_animation_player(Ref p_animation_player) { + animation_player_param = p_animation_player; + emit_changed(); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { + animation_player_param->connect(SNAME("changed"), Callable(this, SNAME("emit_changed"))); + } +} + +void BTPlayAnimation::set_animation_name(StringName p_animation_name) { + animation_name = p_animation_name; + emit_changed(); +} + +void BTPlayAnimation::set_await_completion(double p_await_completion) { + await_completion = p_await_completion; + emit_changed(); +} + +void BTPlayAnimation::set_blend(double p_blend) { + blend = p_blend; + emit_changed(); +} + +void BTPlayAnimation::set_speed(double p_speed) { + speed = p_speed; + emit_changed(); +} + +void BTPlayAnimation::set_from_end(bool p_from_end) { + from_end = p_from_end; + emit_changed(); +} + +//**** Task Implementation + +String BTPlayAnimation::get_configuration_warning() const { + String warning = BTAction::get_configuration_warning(); + if (!warning.is_empty()) { + warning += "\n"; + } + + if (animation_player_param.is_null()) { + warning += "Animation Player parameter is not set.\n"; + } else { + if (animation_player_param->get_value_source() == BBParam::SAVED_VALUE && animation_player_param->get_saved_value().is_zero()) { + warning += "Path to AnimationPlayer node is not set.\n"; + } else if (animation_player_param->get_value_source() == BBParam::BLACKBOARD_VAR && animation_player_param->get_variable().is_empty()) { + warning += "AnimationPlayer blackboard variable is not set.\n"; + } + } + if (animation_name == StringName() && await_completion > 0.0) { + warning += "Animation Name is required in order to wait for the animation to finish.\n"; + } + + return warning; +} + +String BTPlayAnimation::_generate_name() const { + return "PlayAnimation" + + (animation_name != StringName() ? vformat(" \"%s\"", animation_name) : "") + + (blend >= 0.0 ? vformat(" blend: %ss", Math::snapped(blend, 0.001)) : "") + + (speed != 1.0 ? vformat(" speed: %s", Math::snapped(speed, 0.001)) : "") + + (from_end != false ? vformat(" from_end: %s", from_end) : "") + + (await_completion > 0.0 ? vformat(" await_completion: %ss", Math::snapped(await_completion, 0.001)) : ""); +} + +void BTPlayAnimation::_setup() { + setup_failed = true; + ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTPlayAnimation: AnimationPlayer parameter is not set."); + animation_player = Object::cast_to(animation_player_param->get_value(get_agent(), get_blackboard())); + ERR_FAIL_COND_MSG(animation_player == nullptr, "BTPlayAnimation: Failed to get AnimationPlayer."); + ERR_FAIL_COND_MSG(animation_name != StringName() && !animation_player->has_animation(animation_name), vformat("BTPlayAnimation: Animation not found: %s", animation_name)); + if (animation_name == StringName() && await_completion > 0.0) { + WARN_PRINT("BTPlayAnimation: Animation Name is required in order to wait for the animation to finish."); + } + setup_failed = false; +} + +void BTPlayAnimation::_enter() { + if (!setup_failed) { + animation_player->play(animation_name, blend, speed, from_end); + } +} + +int BTPlayAnimation::_tick(double p_delta) { + ERR_FAIL_COND_V_MSG(setup_failed == true, FAILURE, "BTPlayAnimation: _setup() failed - returning FAILURE."); + + // ! Doing this check instead of using signal due to a bug in Godot: https://github.com/godotengine/godot/issues/76127 + if (animation_player->is_playing() && animation_player->get_assigned_animation() == animation_name) { + if (get_elapsed_time() < await_completion) { + return RUNNING; + } else if (await_completion > 0.0) { + WARN_PRINT(vformat("BTPlayAnimation: Waiting time for the \"%s\" animation exceeded the allocated %s sec.", animation_name, await_completion)); + } + } + return SUCCESS; +} + +//**** Godot + +void BTPlayAnimation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_animation_player", "p_anim_player"), &BTPlayAnimation::set_animation_player); + ClassDB::bind_method(D_METHOD("get_animation_player"), &BTPlayAnimation::get_animation_player); + ClassDB::bind_method(D_METHOD("set_animation_name", "p_anim_name"), &BTPlayAnimation::set_animation_name); + ClassDB::bind_method(D_METHOD("get_animation_name"), &BTPlayAnimation::get_animation_name); + ClassDB::bind_method(D_METHOD("set_await_completion", "p_time_sec"), &BTPlayAnimation::set_await_completion); + ClassDB::bind_method(D_METHOD("get_await_completion"), &BTPlayAnimation::get_await_completion); + ClassDB::bind_method(D_METHOD("set_blend", "p_blend"), &BTPlayAnimation::set_blend); + ClassDB::bind_method(D_METHOD("get_blend"), &BTPlayAnimation::get_blend); + ClassDB::bind_method(D_METHOD("set_speed", "p_speed"), &BTPlayAnimation::set_speed); + ClassDB::bind_method(D_METHOD("get_speed"), &BTPlayAnimation::get_speed); + ClassDB::bind_method(D_METHOD("set_from_end", "p_from_end"), &BTPlayAnimation::set_from_end); + ClassDB::bind_method(D_METHOD("get_from_end"), &BTPlayAnimation::get_from_end); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "await_completion", PROPERTY_HINT_RANGE, "0.0,100.0"), "set_await_completion", "get_await_completion"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "animation_player", PROPERTY_HINT_RESOURCE_TYPE, "BBNode"), "set_animation_player", "get_animation_player"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation_name"), "set_animation_name", "get_animation_name"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "blend"), "set_blend", "get_blend"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed"), "set_speed", "get_speed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_end"), "set_from_end", "get_from_end"); +} diff --git a/bt/actions/bt_play_animation.h b/bt/actions/bt_play_animation.h new file mode 100644 index 0000000..eb25908 --- /dev/null +++ b/bt/actions/bt_play_animation.h @@ -0,0 +1,65 @@ +/** + * bt_play_animation.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef BT_PLAY_ANIMATION_H +#define BT_PLAY_ANIMATION_H + +#include "bt_action.h" + +#include "modules/limboai/blackboard/bb_param/bb_node.h" + +#include "scene/animation/animation_player.h" + +class BTPlayAnimation : public BTAction { + GDCLASS(BTPlayAnimation, BTAction); + +private: + Ref animation_player_param; + StringName animation_name; + double await_completion = 0.0; + double blend = -1.0; + double speed = 1.0; + bool from_end = false; + + AnimationPlayer *animation_player = nullptr; + bool setup_failed = false; + +protected: + static void _bind_methods(); + + virtual String _generate_name() const override; + virtual void _setup() override; + virtual void _enter() override; + virtual int _tick(double p_delta) override; + +public: + void set_animation_player(Ref p_animation_player); + Ref get_animation_player() const { return animation_player_param; } + + void set_animation_name(StringName p_animation_name); + StringName get_animation_name() const { return animation_name; } + + void set_await_completion(double p_await_completion); + double get_await_completion() const { return await_completion; } + + void set_blend(double p_blend); + double get_blend() const { return blend; } + + void set_speed(double p_speed); + double get_speed() const { return speed; } + + void set_from_end(bool p_from_end); + bool get_from_end() const { return from_end; } + + virtual String get_configuration_warning() const override; +}; + +#endif // BT_PLAY_ANIMATION \ No newline at end of file diff --git a/bt/actions/bt_stop_animation.cpp b/bt/actions/bt_stop_animation.cpp new file mode 100644 index 0000000..60da2ec --- /dev/null +++ b/bt/actions/bt_stop_animation.cpp @@ -0,0 +1,93 @@ +/** + * bt_stop_animation.cpp + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#include "modules/limboai/bt/actions/bt_stop_animation.h" + +//**** Setters / Getters + +void BTStopAnimation::set_animation_player(Ref p_animation_player) { + animation_player_param = p_animation_player; + emit_changed(); + if (Engine::get_singleton()->is_editor_hint() && animation_player_param.is_valid()) { + animation_player_param->connect(SNAME("changed"), Callable(this, SNAME("emit_changed"))); + } +} + +void BTStopAnimation::set_animation_name(StringName p_animation_name) { + animation_name = p_animation_name; + emit_changed(); +} + +void BTStopAnimation::set_keep_state(bool p_keep_state) { + keep_state = p_keep_state; + emit_changed(); +} + +//**** Task Implementation + +String BTStopAnimation::get_configuration_warning() const { + String warning = BTAction::get_configuration_warning(); + if (!warning.is_empty()) { + warning += "\n"; + } + + if (animation_player_param.is_null()) { + warning += "Animation Player parameter is not set.\n"; + } else { + if (animation_player_param->get_value_source() == BBParam::SAVED_VALUE && animation_player_param->get_saved_value().is_zero()) { + warning += "Path to AnimationPlayer node is not set.\n"; + } else if (animation_player_param->get_value_source() == BBParam::BLACKBOARD_VAR && animation_player_param->get_variable().is_empty()) { + warning += "AnimationPlayer blackboard variable is not set.\n"; + } + } + + return warning; +} + +String BTStopAnimation::_generate_name() const { + return "StopAnimation" + + (animation_name != StringName() ? vformat(" \"%s\"", animation_name) : "") + + (keep_state ? " keep_state: true" : ""); +} + +void BTStopAnimation::_setup() { + setup_failed = true; + ERR_FAIL_COND_MSG(animation_player_param.is_null(), "BTStopAnimation: AnimationPlayer parameter is not set."); + animation_player = Object::cast_to(animation_player_param->get_value(get_agent(), get_blackboard())); + ERR_FAIL_COND_MSG(animation_player == nullptr, "BTStopAnimation: Failed to get AnimationPlayer."); + if (animation_name != StringName()) { + ERR_FAIL_COND_MSG(!animation_player->has_animation(animation_name), vformat("BTStopAnimation: Animation not found: %s", animation_name)); + } + setup_failed = false; +} + +int BTStopAnimation::_tick(double p_delta) { + ERR_FAIL_COND_V_MSG(setup_failed == true, FAILURE, "BTStopAnimation: _setup() failed - returning FAILURE."); + if (animation_player->is_playing() && (animation_name == StringName() || animation_name == animation_player->get_assigned_animation())) { + animation_player->stop(keep_state); + } + return SUCCESS; +} + +//**** Godot + +void BTStopAnimation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_animation_player", "p_anim_player"), &BTStopAnimation::set_animation_player); + ClassDB::bind_method(D_METHOD("get_animation_player"), &BTStopAnimation::get_animation_player); + ClassDB::bind_method(D_METHOD("set_animation_name", "p_anim_name"), &BTStopAnimation::set_animation_name); + ClassDB::bind_method(D_METHOD("get_animation_name"), &BTStopAnimation::get_animation_name); + ClassDB::bind_method(D_METHOD("set_keep_state", "p_keep_state"), &BTStopAnimation::set_keep_state); + ClassDB::bind_method(D_METHOD("get_keep_state"), &BTStopAnimation::get_keep_state); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "animation_player", PROPERTY_HINT_RESOURCE_TYPE, "BBNode"), "set_animation_player", "get_animation_player"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation_name"), "set_animation_name", "get_animation_name"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_state"), "set_keep_state", "get_keep_state"); +} diff --git a/bt/actions/bt_stop_animation.h b/bt/actions/bt_stop_animation.h new file mode 100644 index 0000000..2f9b7cd --- /dev/null +++ b/bt/actions/bt_stop_animation.h @@ -0,0 +1,52 @@ +/** + * bt_stop_animation.h + * ============================================================================= + * Copyright 2021-2023 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef BT_STOP_ANIMATION_H +#define BT_STOP_ANIMATION_H + +#include "bt_action.h" + +#include "modules/limboai/blackboard/bb_param/bb_node.h" + +#include "scene/animation/animation_player.h" + +class BTStopAnimation : public BTAction { + GDCLASS(BTStopAnimation, BTAction); + +private: + Ref animation_player_param; + StringName animation_name; + bool keep_state = false; + + AnimationPlayer *animation_player = nullptr; + bool setup_failed = false; + +protected: + static void _bind_methods(); + + virtual String _generate_name() const override; + virtual void _setup() override; + virtual int _tick(double p_delta) override; + +public: + void set_animation_player(Ref p_animation_player); + Ref get_animation_player() const { return animation_player_param; } + + void set_animation_name(StringName p_animation_name); + StringName get_animation_name() const { return animation_name; } + + void set_keep_state(bool p_keep_state); + bool get_keep_state() const { return keep_state; } + + virtual String get_configuration_warning() const override; +}; + +#endif // BT_STOP_ANIMATION \ No newline at end of file diff --git a/config.py b/config.py index 8ddd132..bee70be 100644 --- a/config.py +++ b/config.py @@ -60,6 +60,7 @@ def get_doc_classes(): "BTAction", "BTAlwaysFail", "BTAlwaysSucceed", + "BTAwaitAnimation", "BTCheckAgentProperty", "BTCheckTrigger", "BTCheckVar", @@ -76,6 +77,8 @@ def get_doc_classes(): "BTInvert", "BTNewScope", "BTParallel", + "BTPauseAnimation", + "BTPlayAnimation", "BTPlayer", "BTProbability", "BTRandomSelector", @@ -90,6 +93,7 @@ def get_doc_classes(): "BTSetAgentProperty", "BTSetVar", "BTState", + "BTStopAnimation", "BTSubtree", "BTTask", "BTTimeLimit", diff --git a/demo/ai/tasks/play_animation.gd b/demo/ai/tasks/play_animation.gd deleted file mode 100644 index ca833da..0000000 --- a/demo/ai/tasks/play_animation.gd +++ /dev/null @@ -1,46 +0,0 @@ -#* -#* play_animation.gd -#* ============================================================================= -#* Copyright 2021-2023 Serhii Snitsaruk -#* -#* Use of this source code is governed by an MIT-style -#* license that can be found in the LICENSE file or at -#* https://opensource.org/licenses/MIT. -#* ============================================================================= -#* - -@tool -extends BTAction - -@export var animation_name: String -@export var animation_player: NodePath - -var _player: AnimationPlayer -var _finished: bool - - -func _generate_name() -> String: - return "PlayAnimation \"%s\"" % animation_name - - -func _setup() -> void: - _player = agent.get_node(animation_player) - - -func _enter() -> void: - if _player.has_animation(animation_name): - _finished = false - _player.play(animation_name) - _player.animation_finished.connect(_on_animation_finished, CONNECT_ONE_SHOT) - else: - _finished = true - - -func _tick(_delta: float) -> int: - if _finished: - return SUCCESS - return RUNNING - - -func _on_animation_finished(_anim): - _finished = true diff --git a/demo/ai/tasks/start_animation.gd b/demo/ai/tasks/start_animation.gd deleted file mode 100644 index a109ff1..0000000 --- a/demo/ai/tasks/start_animation.gd +++ /dev/null @@ -1,31 +0,0 @@ -#* -#* start_animation.gd -#* ============================================================================= -#* Copyright 2021-2023 Serhii Snitsaruk -#* -#* Use of this source code is governed by an MIT-style -#* license that can be found in the LICENSE file or at -#* https://opensource.org/licenses/MIT. -#* ============================================================================= -#* - -@tool -extends BTAction - -@export var animation_name: String -@export var animation_player: NodePath - -var _player: AnimationPlayer - - -func _generate_name() -> String: - return "StartAnimation \"%s\"" % animation_name - - -func _setup() -> void: - _player = agent.get_node(animation_player) - - -func _tick(p_delta: float) -> int: - _player.play(animation_name) - return SUCCESS diff --git a/demo/ai/trees/waypoints.tres b/demo/ai/trees/waypoints.tres index c056b64..ecabd99 100644 --- a/demo/ai/trees/waypoints.tres +++ b/demo/ai/trees/waypoints.tres @@ -1,7 +1,6 @@ [gd_resource type="BehaviorTree" load_steps=10 format=3 uid="uid://cjkqi41oagagd"] [ext_resource type="Script" path="res://ai/tasks/arrive_pos.gd" id="1_rhs33"] -[ext_resource type="Script" path="res://ai/tasks/play_animation.gd" id="2_dg0ss"] [sub_resource type="BTAction" id="BTAction_3xal7"] script = ExtResource("1_rhs33") @@ -9,16 +8,20 @@ target_position_var = "wp" speed_var = "speed" tolerance = 50.0 -[sub_resource type="BTAction" id="BTAction_yq1vl"] -script = ExtResource("2_dg0ss") -animation_name = "bounce" -animation_player = NodePath("AnimationPlayer") +[sub_resource type="BBNode" id="BBNode_0t2vk"] +resource_name = "AnimationPlayer" +saved_value = NodePath("AnimationPlayer") + +[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_s01ov"] +await_completion = 1.0 +animation_player = SubResource("BBNode_0t2vk") +animation_name = &"bounce" [sub_resource type="BTWait" id="BTWait_qs55a"] duration = 0.1 [sub_resource type="BTSequence" id="BTSequence_a2ng0"] -children = [SubResource("BTAction_3xal7"), SubResource("BTAction_yq1vl"), SubResource("BTWait_qs55a")] +children = [SubResource("BTAction_3xal7"), SubResource("BTPlayAnimation_s01ov"), SubResource("BTWait_qs55a")] [sub_resource type="BTForEach" id="BTForEach_0cp04"] children = [SubResource("BTSequence_a2ng0")] diff --git a/doc_classes/BTAwaitAnimation.xml b/doc_classes/BTAwaitAnimation.xml new file mode 100644 index 0000000..886262f --- /dev/null +++ b/doc_classes/BTAwaitAnimation.xml @@ -0,0 +1,24 @@ + + + + BT action that waits for an animation to finish playing. + + + BTAwaitAnimation waits for an animation on the specified [AnimationPlayer] node to finish playing and returns [code]SUCCESS[/code]. + Returns [code]SUCCESS[/code] if the specified animation finished playing or if the specified animation is not currently playing. + Returns [code]FAILURE[/code] if the specified animation doesn't exist or the action failed to get [AnimationPlayer] instance. + + + + + + Animation's key within the [AnimationPlayer] node. + + + Parameter that specifies the [AnimationPlayer] node. + + + Maximum duration to wait for the animation to finish (in seconds). + + + diff --git a/doc_classes/BTPauseAnimation.xml b/doc_classes/BTPauseAnimation.xml new file mode 100644 index 0000000..0f9b767 --- /dev/null +++ b/doc_classes/BTPauseAnimation.xml @@ -0,0 +1,17 @@ + + + + BT action that pauses playback of an animation on the specified [AnimationPlayer] node. + + + BTPauseAnimation action pauses playback of an animation on the specified [AnimationPlayer] node and returns [code]SUCCESS[/code]. + Returns [code]FAILURE[/code] if the action fails to acquire [AnimationPlayer] node. + + + + + + Parameter that specifies the [AnimationPlayer] node. + + + diff --git a/doc_classes/BTPlayAnimation.xml b/doc_classes/BTPlayAnimation.xml new file mode 100644 index 0000000..97bd599 --- /dev/null +++ b/doc_classes/BTPlayAnimation.xml @@ -0,0 +1,34 @@ + + + + BT action that plays an animation on the specified [AnimationPlayer] node. + + + BTPlayAnimation action plays an animation on the specified [AnimationPlayer] node. If the [member await_completion] is greater than [code]0[/code], the action will wait for the animation to complete, with a maximum waiting time equal to [member await_completion]. + Returns [code]SUCCESS[/code] if the animation finishes playing or if the elapsed time exceeds [member await_completion]. When [member await_completion] is set to [code]0[/code], BTPlayAnimation doesn't wait for the animation to finish and immediately returns [code]SUCCESS[/code]. + Returns [code]RUNNING[/code] if the animation is still playing and the elapsed time is less than [member await_completion]. + Returns [code]FAILURE[/code] if the action fails to play the requested animation. + + + + + + Animation's key within the [AnimationPlayer] node. If empty, BTPlayAnimation will resume the last played animation if [AnimationPlayer] was paused. + + + Parameter that specifies the [AnimationPlayer] node. + + + Duration to wait for the animation to finish (in seconds). + + + Custom blend time (in seconds). + + + Play animation from the end. Used in combination with negative [member speed] to play animation in reverse. + + + Custom playback speed scaling ratio. + + + diff --git a/doc_classes/BTStopAnimation.xml b/doc_classes/BTStopAnimation.xml new file mode 100644 index 0000000..c0962ff --- /dev/null +++ b/doc_classes/BTStopAnimation.xml @@ -0,0 +1,23 @@ + + + + BT action that stops playback of an animation on the specified [AnimationPlayer] node. + + + BTStopAnimation action stops playback of an animation on the specified [AnimationPlayer] node and returns [code]SUCCESS[/code]. If [member animation_name] is set, it will only stop playback if the specified animation is currently playing. + Returns [code]FAILURE[/code] if the action fails to acquire [AnimationPlayer] node. + + + + + + Animation's key within the [AnimationPlayer] node. If not empty, BTStopAnimation will only stop playback if the specified animation is currently playing. + + + Parameter that specifies the [AnimationPlayer] node. + + + If [code]true[/code], the animation state is not updated visually. + + + diff --git a/icons/BTAwaitAnimation.svg b/icons/BTAwaitAnimation.svg new file mode 100644 index 0000000..6bb4cd1 --- /dev/null +++ b/icons/BTAwaitAnimation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/BTPauseAnimation.svg b/icons/BTPauseAnimation.svg new file mode 100644 index 0000000..d0878cd --- /dev/null +++ b/icons/BTPauseAnimation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/BTPlayAnimation.svg b/icons/BTPlayAnimation.svg new file mode 100644 index 0000000..d387bf6 --- /dev/null +++ b/icons/BTPlayAnimation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/BTStopAnimation.svg b/icons/BTStopAnimation.svg new file mode 100644 index 0000000..973b542 --- /dev/null +++ b/icons/BTStopAnimation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/register_types.cpp b/register_types.cpp index 80a3074..ce49ceb 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -45,11 +45,15 @@ #include "blackboard/bb_param/bb_vector4i.h" #include "blackboard/blackboard.h" #include "bt/actions/bt_action.h" +#include "bt/actions/bt_await_animation.h" #include "bt/actions/bt_console_print.h" #include "bt/actions/bt_fail.h" +#include "bt/actions/bt_pause_animation.h" +#include "bt/actions/bt_play_animation.h" #include "bt/actions/bt_random_wait.h" #include "bt/actions/bt_set_agent_property.h" #include "bt/actions/bt_set_var.h" +#include "bt/actions/bt_stop_animation.h" #include "bt/actions/bt_wait.h" #include "bt/actions/bt_wait_ticks.h" #include "bt/behavior_tree.h" @@ -139,12 +143,16 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(BTForEach); GDREGISTER_CLASS(BTAction); + GDREGISTER_CLASS(BTAwaitAnimation); GDREGISTER_CLASS(BTConsolePrint); GDREGISTER_CLASS(BTFail); GDREGISTER_CLASS(BTNewScope); + GDREGISTER_CLASS(BTPauseAnimation); + GDREGISTER_CLASS(BTPlayAnimation); GDREGISTER_CLASS(BTRandomWait); GDREGISTER_CLASS(BTSetAgentProperty); GDREGISTER_CLASS(BTSetVar); + GDREGISTER_CLASS(BTStopAnimation); GDREGISTER_CLASS(BTSubtree); GDREGISTER_CLASS(BTWait); GDREGISTER_CLASS(BTWaitTicks);