2023-07-21 09:50:06 +00:00
/**
* bt_player . cpp
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2024-03-04 20:36:16 +00:00
* Copyright 2021 - 2024 Serhii Snitsaruk
2023-07-21 09:50:06 +00:00
*
* 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.
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2022-08-30 16:48:49 +00:00
# include "bt_player.h"
2024-01-10 21:45:42 +00:00
# include "../util/limbo_compat.h"
2024-01-07 01:40:51 +00:00
# include "../util/limbo_string_names.h"
# ifdef LIMBOAI_MODULE
2022-12-15 07:26:52 +00:00
# include "core/config/engine.h"
2023-12-28 01:45:56 +00:00
# include "core/debugger/engine_debugger.h"
2022-12-16 14:29:36 +00:00
# include "core/error/error_macros.h"
2022-08-30 16:48:49 +00:00
# include "core/io/resource_loader.h"
2022-12-15 07:26:52 +00:00
# include "core/object/class_db.h"
2022-09-08 13:56:51 +00:00
# include "core/os/memory.h"
2023-04-15 13:49:34 +00:00
# include "core/string/string_name.h"
2022-12-15 07:26:52 +00:00
# include "core/variant/variant.h"
2023-04-15 13:49:34 +00:00
# include "main/performance.h"
2022-08-30 16:48:49 +00:00
2024-01-07 01:40:51 +00:00
# define IS_DEBUGGER_ACTIVE() (EngineDebugger::is_active())
# define GET_TICKS_USEC() (OS::get_singleton()->get_ticks_usec())
2024-01-13 16:10:42 +00:00
# endif // ! LIMBOAI_MODULE
2024-01-07 01:40:51 +00:00
# ifdef LIMBOAI_GDEXTENSION
# include <godot_cpp/classes/engine_debugger.hpp>
# include <godot_cpp/classes/performance.hpp>
# include <godot_cpp/classes/time.hpp>
# define IS_DEBUGGER_ACTIVE() (EngineDebugger::get_singleton()->is_active())
# define GET_TICKS_USEC() (Time::get_singleton()->get_ticks_usec())
2024-01-13 16:10:42 +00:00
# endif // ! LIMBOAI_GDEXTENSION
2024-01-07 01:40:51 +00:00
2022-08-30 16:48:49 +00:00
VARIANT_ENUM_CAST ( BTPlayer : : UpdateMode ) ;
void BTPlayer : : _load_tree ( ) {
2024-08-03 09:07:06 +00:00
bt_instance . unref ( ) ;
2024-05-01 21:20:17 +00:00
ERR_FAIL_COND_MSG ( ! behavior_tree . is_valid ( ) , " BTPlayer: Initialization failed - needs a valid behavior tree. " ) ;
ERR_FAIL_COND_MSG ( ! behavior_tree - > get_root_task ( ) . is_valid ( ) , " BTPlayer: Initialization failed - behavior tree has no valid root task. " ) ;
2024-05-02 10:11:59 +00:00
Node * agent = GET_NODE ( this , agent_node ) ;
ERR_FAIL_NULL_MSG ( agent , vformat ( " BTPlayer: Initialization failed - can't get agent with path '%s'. " , agent_node ) ) ;
2024-08-05 10:49:58 +00:00
Node * scene_root = scene_root_hint ? scene_root_hint : get_owner ( ) ;
ERR_FAIL_COND_MSG ( scene_root = = nullptr ,
" BTPlayer: Initialization failed - unable to establish scene root. This is likely due to BTPlayer not being owned by a scene node. Check BTPlayer.set_scene_root_hint(). " ) ;
2024-08-03 09:07:06 +00:00
bt_instance = behavior_tree - > instantiate ( agent , blackboard , this ) ;
2024-08-03 14:11:47 +00:00
ERR_FAIL_COND_MSG ( bt_instance . is_null ( ) , " BTPlayer: Failed to instantiate behavior tree. " ) ;
2023-04-13 07:29:45 +00:00
# ifdef DEBUG_ENABLED
2024-08-03 09:07:06 +00:00
bt_instance - > set_monitor_performance ( monitor_performance ) ;
bt_instance - > register_with_debugger ( ) ;
# endif // DEBUG_ENABLED
2022-08-30 16:48:49 +00:00
}
2024-01-23 19:02:23 +00:00
void BTPlayer : : _update_blackboard_plan ( ) {
if ( blackboard_plan . is_null ( ) ) {
blackboard_plan = Ref < BlackboardPlan > ( memnew ( BlackboardPlan ) ) ;
2024-04-01 14:34:36 +00:00
} else if ( ! RESOURCE_IS_BUILT_IN ( blackboard_plan ) ) {
WARN_PRINT_ED ( " BTPlayer: Using external resource for derived blackboard plan is not supported. Converted to built-in resource. " ) ;
blackboard_plan = blackboard_plan - > duplicate ( ) ;
2024-01-23 19:02:23 +00:00
}
2024-04-01 14:34:36 +00:00
2024-01-24 22:11:09 +00:00
blackboard_plan - > set_base_plan ( behavior_tree . is_valid ( ) ? behavior_tree - > get_blackboard_plan ( ) : nullptr ) ;
2024-01-23 14:31:56 +00:00
}
2024-08-04 10:36:44 +00:00
void BTPlayer : : set_bt_instance ( const Ref < BTInstance > & p_bt_instance ) {
ERR_FAIL_COND_MSG ( p_bt_instance . is_null ( ) , " BTPlayer: Failed to set behavior tree instance - instance is null. " ) ;
ERR_FAIL_COND_MSG ( ! p_bt_instance - > is_instance_valid ( ) , " BTPlayer: Failed to set behavior tree instance - instance is not valid. " ) ;
bt_instance = p_bt_instance ;
blackboard = p_bt_instance - > get_blackboard ( ) ;
agent_node = p_bt_instance - > get_agent ( ) - > get_path ( ) ;
blackboard_plan . unref ( ) ;
behavior_tree . unref ( ) ;
}
2024-08-05 10:49:58 +00:00
void BTPlayer : : set_scene_root_hint ( Node * p_scene_root ) {
ERR_FAIL_NULL_MSG ( p_scene_root , " BTPlayer: Failed to set scene root hint - scene root is null. " ) ;
if ( bt_instance . is_valid ( ) ) {
ERR_PRINT ( " BTPlayer: Scene root hint shouldn't be set after the behavior tree is instantiated. This change will not affect the current behavior tree instance. " ) ;
}
scene_root_hint = p_scene_root ;
}
2022-08-30 16:48:49 +00:00
void BTPlayer : : set_behavior_tree ( const Ref < BehaviorTree > & p_tree ) {
2024-01-24 23:23:34 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2024-04-01 14:34:36 +00:00
if ( behavior_tree . is_valid ( ) & & behavior_tree - > is_connected ( LW_NAME ( plan_changed ) , callable_mp ( this , & BTPlayer : : _update_blackboard_plan ) ) ) {
behavior_tree - > disconnect ( LW_NAME ( plan_changed ) , callable_mp ( this , & BTPlayer : : _update_blackboard_plan ) ) ;
2024-01-24 23:23:34 +00:00
}
if ( p_tree . is_valid ( ) ) {
2024-04-01 14:34:36 +00:00
p_tree - > connect ( LW_NAME ( plan_changed ) , callable_mp ( this , & BTPlayer : : _update_blackboard_plan ) ) ;
2024-01-24 23:23:34 +00:00
}
behavior_tree = p_tree ;
_update_blackboard_plan ( ) ;
} else {
behavior_tree = p_tree ;
2024-08-03 12:50:43 +00:00
if ( get_owner ( ) & & is_inside_tree ( ) ) {
2024-01-24 23:23:34 +00:00
_load_tree ( ) ;
}
2022-08-30 16:48:49 +00:00
}
2024-01-23 14:31:56 +00:00
}
2024-05-02 10:49:32 +00:00
void BTPlayer : : set_agent_node ( const NodePath & p_agent_node ) {
agent_node = p_agent_node ;
2024-08-03 09:07:06 +00:00
if ( bt_instance . is_valid ( ) ) {
2024-05-02 10:49:32 +00:00
ERR_PRINT ( " BTPlayer: Agent node cannot be set after the behavior tree is instantiated. This change will not affect the behavior tree instance. " ) ;
}
}
2024-01-23 19:02:23 +00:00
void BTPlayer : : set_blackboard_plan ( const Ref < BlackboardPlan > & p_plan ) {
2024-04-01 14:34:36 +00:00
blackboard_plan = p_plan ;
2024-01-23 19:02:23 +00:00
_update_blackboard_plan ( ) ;
2022-08-30 16:48:49 +00:00
}
void BTPlayer : : set_update_mode ( UpdateMode p_mode ) {
update_mode = p_mode ;
set_active ( active ) ;
}
void BTPlayer : : set_active ( bool p_active ) {
active = p_active ;
2022-12-16 14:29:36 +00:00
bool is_not_editor = ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
set_process ( update_mode = = UpdateMode : : IDLE & & active & & is_not_editor ) ;
set_physics_process ( update_mode = = UpdateMode : : PHYSICS & & active & & is_not_editor ) ;
set_process_input ( active & & is_not_editor ) ;
2022-08-30 16:48:49 +00:00
}
2023-04-10 08:08:11 +00:00
void BTPlayer : : update ( double p_delta ) {
2024-08-03 09:07:06 +00:00
if ( ! bt_instance . is_valid ( ) ) {
2022-12-16 14:29:36 +00:00
ERR_PRINT_ONCE ( vformat ( " BTPlayer doesn't have a behavior tree with a valid root task to execute (owner: %s) " , get_owner ( ) ) ) ;
2022-08-30 16:48:49 +00:00
return ;
}
2023-04-15 13:49:34 +00:00
2022-08-30 16:48:49 +00:00
if ( active ) {
2024-08-03 14:11:47 +00:00
BT : : Status status = bt_instance - > update ( p_delta ) ;
emit_signal ( LW_NAME ( updated ) , status ) ;
# ifndef DISABLE_DEPRECATED
if ( status = = BTTask : : SUCCESS | | status = = BTTask : : FAILURE ) {
emit_signal ( LW_NAME ( behavior_tree_finished ) , status ) ;
}
# endif // DISABLE_DEPRECATED
2022-08-30 16:48:49 +00:00
}
}
void BTPlayer : : restart ( ) {
2024-08-03 09:07:06 +00:00
ERR_FAIL_COND_MSG ( bt_instance . is_null ( ) , " BTPlayer: Restart failed - no valid tree instance. Make sure the BTPlayer has a valid behavior tree with a valid root task. " ) ;
bt_instance - > get_root_task ( ) - > abort ( ) ;
2022-08-30 16:48:49 +00:00
set_active ( true ) ;
}
2023-04-15 13:49:34 +00:00
# ifdef DEBUG_ENABLED
void BTPlayer : : _set_monitor_performance ( bool p_monitor_performance ) {
monitor_performance = p_monitor_performance ;
2024-08-03 09:07:06 +00:00
if ( bt_instance . is_valid ( ) ) {
bt_instance - > set_monitor_performance ( monitor_performance ) ;
2024-06-04 07:30:42 +00:00
}
}
2024-01-13 16:10:42 +00:00
# endif // ! DEBUG_ENABLED
2023-04-15 13:49:34 +00:00
2022-08-30 16:48:49 +00:00
void BTPlayer : : _notification ( int p_notification ) {
switch ( p_notification ) {
case NOTIFICATION_PROCESS : {
2022-12-16 14:29:36 +00:00
Variant time = get_process_delta_time ( ) ;
update ( time ) ;
2022-08-30 16:48:49 +00:00
} break ;
case NOTIFICATION_PHYSICS_PROCESS : {
2023-04-14 08:17:08 +00:00
Variant time = get_physics_process_delta_time ( ) ;
2022-12-16 14:29:36 +00:00
update ( time ) ;
2022-08-30 16:48:49 +00:00
} break ;
case NOTIFICATION_READY : {
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2024-01-23 15:22:10 +00:00
if ( blackboard . is_null ( ) ) {
blackboard = Ref < Blackboard > ( memnew ( Blackboard ) ) ;
}
2024-01-23 19:02:23 +00:00
if ( blackboard_plan . is_valid ( ) ) {
2024-03-06 19:17:23 +00:00
blackboard_plan - > populate_blackboard ( blackboard , false , this ) ;
2024-01-23 11:05:54 +00:00
}
2022-08-30 16:48:49 +00:00
if ( behavior_tree . is_valid ( ) ) {
_load_tree ( ) ;
}
2024-03-11 17:58:40 +00:00
} else {
_update_blackboard_plan ( ) ;
2022-08-30 16:48:49 +00:00
}
2024-07-17 16:25:58 +00:00
set_active ( active ) ;
2022-08-30 16:48:49 +00:00
} break ;
2023-09-09 11:37:09 +00:00
case NOTIFICATION_ENTER_TREE : {
2024-01-24 23:23:34 +00:00
# ifdef DEBUG_ENABLED
2024-08-03 09:07:06 +00:00
if ( bt_instance . is_valid ( ) ) {
bt_instance - > set_monitor_performance ( monitor_performance ) ;
2024-08-03 12:50:43 +00:00
bt_instance - > register_with_debugger ( ) ;
2024-06-04 07:30:42 +00:00
}
2024-01-24 23:23:34 +00:00
# endif // DEBUG_ENABLED
2023-09-09 11:37:09 +00:00
} break ;
2023-04-13 07:29:45 +00:00
case NOTIFICATION_EXIT_TREE : {
2024-01-24 23:23:34 +00:00
# ifdef DEBUG_ENABLED
2024-08-03 09:07:06 +00:00
if ( bt_instance . is_valid ( ) ) {
bt_instance - > set_monitor_performance ( false ) ;
2024-08-03 12:50:43 +00:00
bt_instance - > unregister_with_debugger ( ) ;
2024-06-04 07:30:42 +00:00
}
2024-01-13 16:10:42 +00:00
# endif // DEBUG_ENABLED
2024-01-24 23:23:34 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2024-04-01 14:34:36 +00:00
if ( behavior_tree . is_valid ( ) & & behavior_tree - > is_connected ( LW_NAME ( plan_changed ) , callable_mp ( this , & BTPlayer : : _update_blackboard_plan ) ) ) {
behavior_tree - > disconnect ( LW_NAME ( plan_changed ) , callable_mp ( this , & BTPlayer : : _update_blackboard_plan ) ) ;
2024-01-24 23:23:34 +00:00
}
}
} break ;
2022-08-30 16:48:49 +00:00
}
}
void BTPlayer : : _bind_methods ( ) {
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_behavior_tree " , " behavior_tree " ) , & BTPlayer : : set_behavior_tree ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_behavior_tree " ) , & BTPlayer : : get_behavior_tree ) ;
2024-05-02 10:11:59 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_agent_node " , " agent_node " ) , & BTPlayer : : set_agent_node ) ;
ClassDB : : bind_method ( D_METHOD ( " get_agent_node " ) , & BTPlayer : : get_agent_node ) ;
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_update_mode " , " update_mode " ) , & BTPlayer : : set_update_mode ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_update_mode " ) , & BTPlayer : : get_update_mode ) ;
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_active " , " active " ) , & BTPlayer : : set_active ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_active " ) , & BTPlayer : : get_active ) ;
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_blackboard " , " blackboard " ) , & BTPlayer : : set_blackboard ) ;
2022-09-05 14:30:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_blackboard " ) , & BTPlayer : : get_blackboard ) ;
2022-08-30 16:48:49 +00:00
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_blackboard_plan " , " plan " ) , & BTPlayer : : set_blackboard_plan ) ;
2024-01-23 19:02:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_blackboard_plan " ) , & BTPlayer : : get_blackboard_plan ) ;
2022-09-08 13:56:51 +00:00
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " update " , " delta " ) , & BTPlayer : : update ) ;
2022-08-30 16:48:49 +00:00
ClassDB : : bind_method ( D_METHOD ( " restart " ) , & BTPlayer : : restart ) ;
2024-08-03 09:07:06 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_bt_instance " ) , & BTPlayer : : get_bt_instance ) ;
2024-08-04 10:36:44 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_bt_instance " , " bt_instance " ) , & BTPlayer : : set_bt_instance ) ;
2024-02-03 15:31:21 +00:00
2024-08-05 10:49:58 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_scene_root_hint " , " scene_root " ) , & BTPlayer : : set_scene_root_hint ) ;
2022-08-30 16:48:49 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " behavior_tree " , PROPERTY_HINT_RESOURCE_TYPE , " BehaviorTree " ) , " set_behavior_tree " , " get_behavior_tree " ) ;
2024-05-02 10:11:59 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " agent_node " ) , " set_agent_node " , " get_agent_node " ) ;
2022-08-30 16:48:49 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " update_mode " , PROPERTY_HINT_ENUM , " Idle,Physics,Manual " ) , " set_update_mode " , " get_update_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " active " ) , " set_active " , " get_active " ) ;
2023-10-26 11:44:09 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " blackboard " , PROPERTY_HINT_NONE , " Blackboard " , 0 ) , " set_blackboard " , " get_blackboard " ) ;
2024-03-31 23:11:51 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " blackboard_plan " , PROPERTY_HINT_RESOURCE_TYPE , " BlackboardPlan " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT | PROPERTY_USAGE_ALWAYS_DUPLICATE ) , " set_blackboard_plan " , " get_blackboard_plan " ) ;
2022-08-30 16:48:49 +00:00
BIND_ENUM_CONSTANT ( IDLE ) ;
BIND_ENUM_CONSTANT ( PHYSICS ) ;
BIND_ENUM_CONSTANT ( MANUAL ) ;
2022-08-31 15:50:49 +00:00
2024-03-04 20:36:16 +00:00
ADD_SIGNAL ( MethodInfo ( " updated " , PropertyInfo ( Variant : : INT , " status " ) ) ) ;
2023-04-15 13:49:34 +00:00
2024-08-03 14:11:47 +00:00
# ifndef DISABLE_DEPRECATED
ADD_SIGNAL ( MethodInfo ( " behavior_tree_finished " , PropertyInfo ( Variant : : INT , " status " ) ) ) ;
# endif
2023-04-15 13:49:34 +00:00
# ifdef DEBUG_ENABLED
2024-03-04 20:36:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " _set_monitor_performance " , " enable " ) , & BTPlayer : : _set_monitor_performance ) ;
2023-04-15 13:49:34 +00:00
ClassDB : : bind_method ( D_METHOD ( " _get_monitor_performance " ) , & BTPlayer : : _get_monitor_performance ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " monitor_performance " ) , " _set_monitor_performance " , " _get_monitor_performance " ) ;
# endif // DEBUG_ENABLED
2022-09-08 13:56:51 +00:00
}
BTPlayer : : BTPlayer ( ) {
2024-01-23 15:22:10 +00:00
blackboard = Ref < Blackboard > ( memnew ( Blackboard ) ) ;
2024-05-02 10:11:59 +00:00
agent_node = LW_NAME ( node_pp ) ;
2022-09-08 13:56:51 +00:00
}
BTPlayer : : ~ BTPlayer ( ) {
2023-04-15 13:49:34 +00:00
}