2024-01-23 09:33:57 +00:00
/**
2024-01-23 19:02:23 +00:00
* blackboard_plan . cpp
2024-01-23 09:33:57 +00:00
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* Copyright 2021 - 2024 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.
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2024-01-23 19:02:23 +00:00
# include "blackboard_plan.h"
2024-01-23 09:33:57 +00:00
2024-05-15 10:50:33 +00:00
# include "../util/limbo_utility.h"
2024-11-11 08:49:01 +00:00
# ifdef LIMBOAI_MODULE
# include "editor/editor_inspector.h"
# include "editor/editor_interface.h"
# elif LIMBOAI_GDEXTENSION
# include <godot_cpp/classes/editor_inspector.hpp>
# include <godot_cpp/classes/editor_interface.hpp>
# include <godot_cpp/classes/engine.hpp>
# include <godot_cpp/classes/scene_tree.hpp>
# endif
2024-01-23 19:02:23 +00:00
bool BlackboardPlan : : _set ( const StringName & p_name , const Variant & p_value ) {
2024-03-04 15:55:08 +00:00
String name_str = p_name ;
2024-01-23 14:31:56 +00:00
2024-11-11 10:43:26 +00:00
# ifdef TOOLS_ENABLED
2024-01-23 14:31:56 +00:00
// * Editor
2024-03-04 15:55:08 +00:00
if ( var_map . has ( p_name ) ) {
2024-03-24 23:56:08 +00:00
BBVariable & var = var_map [ p_name ] ;
var . set_value ( p_value ) ;
2024-03-04 15:55:08 +00:00
if ( base . is_valid ( ) & & p_value = = base - > get_var ( p_name ) . get_value ( ) ) {
2024-02-09 12:58:54 +00:00
// When user pressed reset property button in inspector...
2024-03-24 23:56:08 +00:00
var . reset_value_changed ( ) ;
2024-02-09 12:58:54 +00:00
}
2024-01-23 14:31:56 +00:00
return true ;
}
2024-11-11 10:43:26 +00:00
# endif // TOOLS_ENABLED
2024-05-14 07:29:56 +00:00
// * Mapping
if ( name_str . begins_with ( " mapping/ " ) ) {
StringName mapped_var_name = name_str . get_slicec ( ' / ' , 1 ) ;
2024-05-14 18:25:18 +00:00
StringName value = p_value ;
2024-11-10 18:08:56 +00:00
bool prop_list_changed = false ;
2024-05-14 18:25:18 +00:00
if ( value = = StringName ( ) ) {
2024-05-17 19:48:34 +00:00
if ( parent_scope_mapping . has ( mapped_var_name ) ) {
2024-11-10 18:08:56 +00:00
prop_list_changed = true ;
2024-05-17 19:48:34 +00:00
parent_scope_mapping . erase ( mapped_var_name ) ;
}
2024-05-14 18:25:18 +00:00
} else {
2024-05-17 19:48:34 +00:00
if ( ! parent_scope_mapping . has ( mapped_var_name ) ) {
2024-11-10 18:08:56 +00:00
prop_list_changed = true ;
2024-05-17 19:48:34 +00:00
}
2024-05-14 18:25:18 +00:00
parent_scope_mapping [ mapped_var_name ] = value ;
}
2024-11-10 18:08:56 +00:00
if ( prop_list_changed ) {
2024-05-17 19:48:34 +00:00
notify_property_list_changed ( ) ;
}
2024-05-13 21:21:55 +00:00
return true ;
}
2024-01-23 14:31:56 +00:00
2024-11-10 18:08:56 +00:00
// * Binding
if ( name_str . begins_with ( " binding/ " ) ) {
StringName bound_var = name_str . get_slicec ( ' / ' , 1 ) ;
NodePath value = p_value ;
bool prop_list_changed = false ;
if ( value . is_empty ( ) ) {
if ( property_bindings . has ( bound_var ) ) {
prop_list_changed = true ;
property_bindings . erase ( bound_var ) ;
}
} else {
if ( ! property_bindings . has ( bound_var ) ) {
prop_list_changed = true ;
}
property_bindings [ bound_var ] = value ;
}
if ( prop_list_changed ) {
notify_property_list_changed ( ) ;
}
}
2024-01-23 14:31:56 +00:00
// * Storage
2024-03-04 15:55:08 +00:00
if ( name_str . begins_with ( " var/ " ) ) {
StringName var_name = name_str . get_slicec ( ' / ' , 1 ) ;
String what = name_str . get_slicec ( ' / ' , 2 ) ;
2024-01-25 16:59:38 +00:00
if ( ! var_map . has ( var_name ) & & what = = " name " ) {
add_var ( var_name , BBVariable ( ) ) ;
2024-01-23 14:31:56 +00:00
}
if ( what = = " name " ) {
// We don't store variable name with the variable.
} else if ( what = = " type " ) {
2024-01-25 16:59:38 +00:00
var_map [ var_name ] . set_type ( ( Variant : : Type ) ( int ) p_value ) ;
2024-01-23 14:31:56 +00:00
} else if ( what = = " value " ) {
2024-01-25 16:59:38 +00:00
var_map [ var_name ] . set_value ( p_value ) ;
2024-01-23 14:31:56 +00:00
} else if ( what = = " hint " ) {
2024-01-25 16:59:38 +00:00
var_map [ var_name ] . set_hint ( ( PropertyHint ) ( int ) p_value ) ;
2024-01-23 14:31:56 +00:00
} else if ( what = = " hint_string " ) {
2024-01-25 16:59:38 +00:00
var_map [ var_name ] . set_hint_string ( p_value ) ;
2024-11-10 18:08:56 +00:00
} else if ( what = = " property_binding " ) {
property_bindings [ var_name ] = NodePath ( p_value ) ;
2024-01-23 14:31:56 +00:00
} else {
return false ;
}
return true ;
}
return false ;
}
2024-01-23 19:02:23 +00:00
bool BlackboardPlan : : _get ( const StringName & p_name , Variant & r_ret ) const {
2024-03-04 15:55:08 +00:00
String name_str = p_name ;
2024-01-23 14:31:56 +00:00
2024-11-11 10:43:26 +00:00
# ifdef TOOLS_ENABLED
2024-01-23 14:31:56 +00:00
// * Editor
2024-03-04 15:55:08 +00:00
if ( var_map . has ( p_name ) ) {
2024-05-13 21:21:55 +00:00
if ( has_mapping ( p_name ) ) {
2024-05-15 10:50:33 +00:00
r_ret = " Mapped to " + LimboUtility : : get_singleton ( ) - > decorate_var ( parent_scope_mapping [ p_name ] ) ;
2024-11-10 18:08:56 +00:00
} else if ( has_property_binding ( p_name ) ) {
2024-11-11 07:24:54 +00:00
const NodePath & binding = property_bindings [ p_name ] ;
2024-11-11 08:49:01 +00:00
Node * edited_node = Object : : cast_to < Node > ( EditorInterface : : get_singleton ( ) - > get_inspector ( ) - > get_edited_object ( ) ) ;
if ( ! edited_node ) {
edited_node = SCENE_TREE ( ) - > get_edited_scene_root ( ) ;
}
2024-11-11 09:23:08 +00:00
Node * bound_node = edited_node ? edited_node - > get_node_or_null ( binding ) : nullptr ;
2024-11-11 08:49:01 +00:00
String shortened_path ;
if ( bound_node ) {
shortened_path = ( String ) bound_node - > get_name ( ) +
" : " + ( String ) binding . get_concatenated_subnames ( ) ;
} else {
shortened_path = ( String ) binding . get_name ( binding . get_name_count ( ) - 1 ) +
" : " + ( String ) binding . get_concatenated_subnames ( ) ;
}
r_ret = String : : utf8 ( " 🔗 " ) + shortened_path ;
2024-05-13 21:21:55 +00:00
} else {
r_ret = var_map [ p_name ] . get_value ( ) ;
}
return true ;
}
2024-11-11 10:43:26 +00:00
# endif // TOOLS_ENABLED
2024-05-14 07:29:56 +00:00
// * Mapping
if ( name_str . begins_with ( " mapping/ " ) ) {
StringName mapped_var_name = name_str . get_slicec ( ' / ' , 1 ) ;
ERR_FAIL_COND_V ( mapped_var_name = = StringName ( ) , false ) ;
2024-11-15 17:00:55 +00:00
if ( has_mapping ( mapped_var_name ) ) {
r_ret = parent_scope_mapping [ mapped_var_name ] ;
} else if ( has_property_binding ( mapped_var_name ) ) {
r_ret = RTR ( " Already bound to property. " ) ;
} else {
r_ret = StringName ( ) ;
}
2024-11-10 18:08:56 +00:00
return true ;
}
// * Binding
if ( name_str . begins_with ( " binding/ " ) ) {
StringName bound_var = name_str . get_slicec ( ' / ' , 1 ) ;
ERR_FAIL_COND_V ( bound_var = = StringName ( ) , false ) ;
2024-11-15 17:00:55 +00:00
if ( has_property_binding ( bound_var ) ) {
r_ret = property_bindings [ bound_var ] ;
} else if ( has_mapping ( bound_var ) ) {
r_ret = RTR ( " Already mapped to variable. " ) ;
} else {
r_ret = NodePath ( ) ;
}
2024-01-23 14:31:56 +00:00
return true ;
}
// * Storage
2024-03-04 15:55:08 +00:00
if ( ! name_str . begins_with ( " var/ " ) ) {
2024-01-23 14:31:56 +00:00
return false ;
}
2024-03-04 15:55:08 +00:00
StringName var_name = name_str . get_slicec ( ' / ' , 1 ) ;
String what = name_str . get_slicec ( ' / ' , 2 ) ;
2024-01-25 16:59:38 +00:00
ERR_FAIL_COND_V ( ! var_map . has ( var_name ) , false ) ;
2024-01-25 13:27:14 +00:00
if ( what = = " name " ) {
r_ret = var_name ;
} else if ( what = = " type " ) {
2024-01-25 16:59:38 +00:00
r_ret = var_map [ var_name ] . get_type ( ) ;
2024-01-23 14:31:56 +00:00
} else if ( what = = " value " ) {
2024-01-25 16:59:38 +00:00
r_ret = var_map [ var_name ] . get_value ( ) ;
2024-01-23 14:31:56 +00:00
} else if ( what = = " hint " ) {
2024-01-25 16:59:38 +00:00
r_ret = var_map [ var_name ] . get_hint ( ) ;
2024-01-23 14:31:56 +00:00
} else if ( what = = " hint_string " ) {
2024-01-25 16:59:38 +00:00
r_ret = var_map [ var_name ] . get_hint_string ( ) ;
2024-01-23 14:31:56 +00:00
}
return true ;
}
2024-01-23 19:02:23 +00:00
void BlackboardPlan : : _get_property_list ( List < PropertyInfo > * p_list ) const {
2024-03-04 15:55:08 +00:00
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-01-25 16:59:38 +00:00
String var_name = p . first ;
BBVariable var = p . second ;
2024-01-23 14:31:56 +00:00
2024-11-11 10:43:26 +00:00
# ifdef TOOLS_ENABLED
2024-01-23 14:31:56 +00:00
// * Editor
2024-11-15 17:50:42 +00:00
if ( ! _is_var_hidden ( var_name , var ) ) {
2024-11-10 18:08:56 +00:00
if ( has_mapping ( var_name ) | | has_property_binding ( var_name ) ) {
2024-05-13 21:21:55 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : STRING , var_name , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY ) ) ;
} else {
p_list - > push_back ( PropertyInfo ( var . get_type ( ) , var_name , var . get_hint ( ) , var . get_hint_string ( ) , PROPERTY_USAGE_EDITOR ) ) ;
}
2024-01-25 10:51:35 +00:00
}
2024-11-11 10:43:26 +00:00
# endif // TOOLS_ENABLED
2024-01-23 14:31:56 +00:00
2024-05-13 21:21:55 +00:00
// * Storage
2024-03-24 23:56:08 +00:00
if ( is_derived ( ) & & ( ! var . is_value_changed ( ) | | var . get_value ( ) = = base - > var_map [ var_name ] . get_value ( ) ) ) {
// Don't store variable if it's not modified in a derived plan.
// Variable is considered modified when it's marked as changed and its value is different from the base plan.
continue ;
}
2024-01-23 14:31:56 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : STRING , " var/ " + var_name + " /name " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : INT , " var/ " + var_name + " /type " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
p_list - > push_back ( PropertyInfo ( var . get_type ( ) , " var/ " + var_name + " /value " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : INT , " var/ " + var_name + " /hint " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : STRING , " var/ " + var_name + " /hint_string " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
}
2024-05-13 21:21:55 +00:00
// * Mapping
2024-05-14 09:39:32 +00:00
if ( is_mapping_enabled ( ) ) {
2024-05-14 07:29:56 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : NIL , " Mapping " , PROPERTY_HINT_NONE , " mapping/ " , PROPERTY_USAGE_GROUP ) ) ;
2024-05-13 21:21:55 +00:00
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-11-15 17:50:42 +00:00
if ( _is_var_hidden ( p . first , p . second ) ) {
continue ;
}
2024-11-15 17:00:55 +00:00
if ( unlikely ( has_property_binding ( p . first ) ) ) {
p_list - > push_back ( PropertyInfo ( Variant : : STRING , " mapping/ " + p . first , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY ) ) ;
} else {
// Serialize only non-empty mappings.
PropertyUsageFlags usage = has_mapping ( p . first ) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR ;
p_list - > push_back ( PropertyInfo ( Variant : : STRING_NAME , " mapping/ " + p . first , PROPERTY_HINT_NONE , " " , usage ) ) ;
}
2024-05-13 21:21:55 +00:00
}
}
2024-11-10 18:08:56 +00:00
// * Binding
p_list - > push_back ( PropertyInfo ( Variant : : NIL , " Binding " , PROPERTY_HINT_NONE , " binding/ " , PROPERTY_USAGE_GROUP ) ) ;
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-11-15 17:50:42 +00:00
if ( _is_var_hidden ( p . first , p . second ) ) {
continue ;
}
2024-11-15 17:00:55 +00:00
if ( unlikely ( has_mapping ( p . first ) ) ) {
p_list - > push_back ( PropertyInfo ( Variant : : STRING , " binding/ " + p . first , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY ) ) ;
} else {
PropertyUsageFlags usage = has_property_binding ( p . first ) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR ;
// PROPERTY_HINT_LINK is used to signal that NodePath should point to a property.
// Our inspector plugin will know how to handle it.
p_list - > push_back ( PropertyInfo ( Variant : : NODE_PATH , " binding/ " + p . first , PROPERTY_HINT_LINK , itos ( p . second . get_type ( ) ) , usage ) ) ;
}
2024-11-10 18:08:56 +00:00
}
2024-01-23 14:31:56 +00:00
}
2024-01-23 19:02:23 +00:00
bool BlackboardPlan : : _property_can_revert ( const StringName & p_name ) const {
2024-05-14 18:25:18 +00:00
if ( String ( p_name ) . begins_with ( " mapping/ " ) ) {
return true ;
}
2024-01-25 16:59:38 +00:00
return base . is_valid ( ) & & base - > var_map . has ( p_name ) ;
2024-01-23 16:54:20 +00:00
}
2024-01-23 19:02:23 +00:00
bool BlackboardPlan : : _property_get_revert ( const StringName & p_name , Variant & r_property ) const {
2024-05-14 18:25:18 +00:00
if ( String ( p_name ) . begins_with ( " mapping/ " ) ) {
r_property = StringName ( ) ;
return true ;
}
2024-01-25 16:59:38 +00:00
if ( base - > var_map . has ( p_name ) ) {
r_property = base - > var_map [ p_name ] . get_value ( ) ;
2024-01-23 16:54:20 +00:00
return true ;
}
return false ;
}
2024-01-23 19:02:23 +00:00
void BlackboardPlan : : set_base_plan ( const Ref < BlackboardPlan > & p_base ) {
2024-03-31 23:45:40 +00:00
if ( p_base = = this ) {
2024-04-01 13:06:06 +00:00
WARN_PRINT_ED ( " BlackboardPlan: Using same resource for derived blackboard plan is not supported. " ) ;
2024-03-31 23:45:40 +00:00
base . unref ( ) ;
} else {
base = p_base ;
}
2024-01-23 19:02:23 +00:00
sync_with_base_plan ( ) ;
2024-02-29 14:45:59 +00:00
notify_property_list_changed ( ) ;
2024-01-23 14:31:56 +00:00
}
2024-05-15 09:38:53 +00:00
void BlackboardPlan : : set_parent_scope_plan_provider ( const Callable & p_parent_scope_plan_provider ) {
parent_scope_plan_provider = p_parent_scope_plan_provider ;
2024-05-14 09:39:32 +00:00
notify_property_list_changed ( ) ;
2024-05-13 21:21:55 +00:00
}
bool BlackboardPlan : : has_mapping ( const StringName & p_name ) const {
2024-05-14 09:39:32 +00:00
return is_mapping_enabled ( ) & & parent_scope_mapping . has ( p_name ) & & parent_scope_mapping [ p_name ] ! = StringName ( ) ;
2024-05-13 21:21:55 +00:00
}
2024-11-10 18:08:56 +00:00
void BlackboardPlan : : set_property_binding ( const StringName & p_name , const NodePath & p_path ) {
property_bindings [ p_name ] = p_path ;
emit_changed ( ) ;
}
2024-03-06 19:17:23 +00:00
void BlackboardPlan : : set_prefetch_nodepath_vars ( bool p_enable ) {
prefetch_nodepath_vars = p_enable ;
emit_changed ( ) ;
}
bool BlackboardPlan : : is_prefetching_nodepath_vars ( ) const {
if ( is_derived ( ) ) {
return base - > is_prefetching_nodepath_vars ( ) ;
} else {
return prefetch_nodepath_vars ;
}
}
2024-03-04 15:55:08 +00:00
void BlackboardPlan : : add_var ( const StringName & p_name , const BBVariable & p_var ) {
2024-03-25 00:04:07 +00:00
ERR_FAIL_COND ( p_name = = StringName ( ) ) ;
2024-01-25 16:59:38 +00:00
ERR_FAIL_COND ( var_map . has ( p_name ) ) ;
var_map . insert ( p_name , p_var ) ;
2024-03-04 15:55:08 +00:00
var_list . push_back ( Pair < StringName , BBVariable > ( p_name , p_var ) ) ;
2024-01-24 22:11:09 +00:00
notify_property_list_changed ( ) ;
emit_changed ( ) ;
2024-01-23 09:33:57 +00:00
}
2024-03-04 15:55:08 +00:00
void BlackboardPlan : : remove_var ( const StringName & p_name ) {
2024-01-25 16:59:38 +00:00
ERR_FAIL_COND ( ! var_map . has ( p_name ) ) ;
2024-03-04 15:55:08 +00:00
var_list . erase ( Pair < StringName , BBVariable > ( p_name , var_map [ p_name ] ) ) ;
2024-01-25 16:59:38 +00:00
var_map . erase ( p_name ) ;
2024-01-24 22:11:09 +00:00
notify_property_list_changed ( ) ;
emit_changed ( ) ;
2024-01-23 09:33:57 +00:00
}
2024-03-04 15:55:08 +00:00
BBVariable BlackboardPlan : : get_var ( const StringName & p_name ) {
2024-01-25 16:59:38 +00:00
ERR_FAIL_COND_V ( ! var_map . has ( p_name ) , BBVariable ( ) ) ;
return var_map . get ( p_name ) ;
2024-01-23 09:33:57 +00:00
}
2024-03-04 15:55:08 +00:00
Pair < StringName , BBVariable > BlackboardPlan : : get_var_by_index ( int p_index ) {
Pair < StringName , BBVariable > ret ;
2024-01-25 16:59:38 +00:00
ERR_FAIL_INDEX_V ( p_index , ( int ) var_map . size ( ) , ret ) ;
2024-05-07 13:17:04 +00:00
return var_list . get ( p_index ) ;
2024-01-24 22:11:09 +00:00
}
2024-05-31 09:57:27 +00:00
TypedArray < StringName > BlackboardPlan : : list_vars ( ) const {
TypedArray < StringName > ret ;
2024-03-04 15:55:08 +00:00
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-01-25 16:59:38 +00:00
ret . append ( p . first ) ;
2024-01-23 09:33:57 +00:00
}
return ret ;
}
2024-03-04 15:55:08 +00:00
StringName BlackboardPlan : : get_var_name ( const BBVariable & p_var ) const {
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-01-25 16:59:38 +00:00
if ( p . second = = p_var ) {
return p . first ;
2024-01-24 22:11:09 +00:00
}
}
2024-03-04 15:55:08 +00:00
return StringName ( ) ;
2024-01-24 22:11:09 +00:00
}
2024-03-04 15:55:08 +00:00
bool BlackboardPlan : : is_valid_var_name ( const StringName & p_name ) const {
String name_str = p_name ;
if ( name_str . begins_with ( " resource_ " ) ) {
2024-01-29 09:32:32 +00:00
return false ;
}
2024-03-04 15:55:08 +00:00
return name_str . is_valid_identifier ( ) & & ! var_map . has ( p_name ) ;
2024-01-28 19:14:52 +00:00
}
2024-03-04 15:55:08 +00:00
void BlackboardPlan : : rename_var ( const StringName & p_name , const StringName & p_new_name ) {
2024-01-28 19:14:52 +00:00
if ( p_name = = p_new_name ) {
return ;
}
ERR_FAIL_COND ( ! is_valid_var_name ( p_new_name ) ) ;
2024-01-25 16:59:38 +00:00
ERR_FAIL_COND ( ! var_map . has ( p_name ) ) ;
2024-03-25 00:04:07 +00:00
ERR_FAIL_COND ( var_map . has ( p_new_name ) ) ;
2024-01-25 16:59:38 +00:00
BBVariable var = var_map [ p_name ] ;
2024-03-04 15:55:08 +00:00
Pair < StringName , BBVariable > new_entry ( p_new_name , var ) ;
Pair < StringName , BBVariable > old_entry ( p_name , var ) ;
2024-01-25 16:59:38 +00:00
var_list . find ( old_entry ) - > set ( new_entry ) ;
var_map . erase ( p_name ) ;
var_map . insert ( p_new_name , var ) ;
2024-01-24 22:11:09 +00:00
2024-05-17 08:00:12 +00:00
if ( parent_scope_mapping . has ( p_name ) ) {
parent_scope_mapping [ p_new_name ] = parent_scope_mapping [ p_name ] ;
parent_scope_mapping . erase ( p_name ) ;
}
2024-01-24 22:11:09 +00:00
notify_property_list_changed ( ) ;
emit_changed ( ) ;
}
2024-01-25 21:01:14 +00:00
void BlackboardPlan : : move_var ( int p_index , int p_new_index ) {
ERR_FAIL_INDEX ( p_index , ( int ) var_map . size ( ) ) ;
ERR_FAIL_INDEX ( p_new_index , ( int ) var_map . size ( ) ) ;
2024-01-24 22:11:09 +00:00
2024-01-25 21:01:14 +00:00
if ( p_index = = p_new_index ) {
return ;
}
2024-03-04 15:55:08 +00:00
List < Pair < StringName , BBVariable > > : : Element * E = var_list . front ( ) ;
2024-01-25 21:01:14 +00:00
for ( int i = 0 ; i < p_index ; i + + ) {
E = E - > next ( ) ;
}
2024-03-04 15:55:08 +00:00
List < Pair < StringName , BBVariable > > : : Element * E2 = var_list . front ( ) ;
2024-01-25 21:01:14 +00:00
for ( int i = 0 ; i < p_new_index ; i + + ) {
E2 = E2 - > next ( ) ;
}
2024-01-24 22:11:09 +00:00
2024-01-25 21:01:14 +00:00
var_list . move_before ( E , E2 ) ;
if ( p_new_index > p_index ) {
var_list . move_before ( E2 , E ) ;
}
2024-01-24 22:11:09 +00:00
notify_property_list_changed ( ) ;
emit_changed ( ) ;
}
2024-01-23 19:02:23 +00:00
void BlackboardPlan : : sync_with_base_plan ( ) {
2024-01-23 16:54:20 +00:00
if ( base . is_null ( ) ) {
return ;
}
2024-01-23 19:02:23 +00:00
2024-01-24 22:11:09 +00:00
bool changed = false ;
2024-01-23 19:02:23 +00:00
// Sync variables with the base plan.
2024-03-04 15:55:08 +00:00
for ( const Pair < StringName , BBVariable > & p : base - > var_list ) {
const StringName & base_name = p . first ;
2024-01-25 16:59:38 +00:00
const BBVariable & base_var = p . second ;
if ( ! var_map . has ( base_name ) ) {
add_var ( base_name , base_var . duplicate ( ) ) ;
2024-01-24 22:11:09 +00:00
changed = true ;
2024-01-23 09:33:57 +00:00
continue ;
}
2024-01-25 16:59:38 +00:00
BBVariable var = var_map [ base_name ] ;
if ( ! var . is_same_prop_info ( base_var ) ) {
var . copy_prop_info ( base_var ) ;
2024-01-24 22:11:09 +00:00
changed = true ;
2024-01-23 09:33:57 +00:00
}
2024-02-09 12:58:54 +00:00
if ( ( ! var . is_value_changed ( ) & & var . get_value ( ) ! = base_var . get_value ( ) ) | |
( var . get_value ( ) . get_type ( ) ! = base_var . get_type ( ) ) ) {
// Reset value according to base plan.
2024-01-25 16:59:38 +00:00
var . set_value ( base_var . get_value ( ) ) ;
2024-02-09 12:58:54 +00:00
var . reset_value_changed ( ) ;
2024-01-24 22:11:09 +00:00
changed = true ;
2024-01-23 09:33:57 +00:00
}
}
2024-01-23 19:02:23 +00:00
// Erase variables that do not exist in the base plan.
2024-03-25 00:04:07 +00:00
List < StringName > erase_list ;
2024-03-04 15:55:08 +00:00
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-01-25 16:59:38 +00:00
if ( ! base - > has_var ( p . first ) ) {
2024-03-25 00:04:07 +00:00
erase_list . push_back ( p . first ) ;
2024-01-24 22:11:09 +00:00
changed = true ;
2024-01-23 19:02:23 +00:00
}
}
2024-03-25 00:04:07 +00:00
while ( erase_list . size ( ) ) {
remove_var ( erase_list . front ( ) - > get ( ) ) ;
erase_list . pop_front ( ) ;
}
2024-01-24 22:11:09 +00:00
2024-03-12 16:11:01 +00:00
// Sync order of variables.
// Glossary: E - element of current plan, B - element of base plan, F - element of current plan (used for forward search).
ERR_FAIL_COND ( base - > var_list . size ( ) ! = var_list . size ( ) ) ;
List < Pair < StringName , BBVariable > > : : Element * B = base - > var_list . front ( ) ;
for ( List < Pair < StringName , BBVariable > > : : Element * E = var_list . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . first ! = B - > get ( ) . first ) {
2024-03-12 20:09:28 +00:00
List < Pair < StringName , BBVariable > > : : Element * F = E - > next ( ) ;
while ( F ) {
2024-03-12 16:11:01 +00:00
if ( F - > get ( ) . first = = B - > get ( ) . first ) {
var_list . move_before ( F , E ) ;
E = F ;
break ;
}
2024-03-12 20:09:28 +00:00
F = F - > next ( ) ;
2024-03-12 16:11:01 +00:00
}
}
B = B - > next ( ) ;
}
2024-01-24 22:11:09 +00:00
if ( changed ) {
notify_property_list_changed ( ) ;
emit_changed ( ) ;
}
2024-01-23 09:33:57 +00:00
}
2024-09-15 12:16:54 +00:00
Ref < Blackboard > BlackboardPlan : : create_blackboard ( Node * p_prefetch_root , const Ref < Blackboard > & p_parent_scope , Node * p_prefetch_root_for_base_plan ) {
ERR_FAIL_COND_V ( p_prefetch_root = = nullptr & & prefetch_nodepath_vars , memnew ( Blackboard ) ) ;
2024-01-23 09:33:57 +00:00
Ref < Blackboard > bb = memnew ( Blackboard ) ;
2024-05-13 21:21:55 +00:00
bb - > set_parent ( p_parent_scope ) ;
2024-09-15 12:16:54 +00:00
populate_blackboard ( bb , true , p_prefetch_root , p_prefetch_root_for_base_plan ) ;
2024-01-23 09:33:57 +00:00
return bb ;
}
2024-01-23 11:05:54 +00:00
2024-09-15 12:16:54 +00:00
void BlackboardPlan : : populate_blackboard ( const Ref < Blackboard > & p_blackboard , bool overwrite , Node * p_prefetch_root , Node * p_prefetch_root_for_base_plan ) {
ERR_FAIL_COND ( p_prefetch_root = = nullptr & & prefetch_nodepath_vars ) ;
2024-08-14 18:07:42 +00:00
ERR_FAIL_COND ( p_blackboard . is_null ( ) ) ;
2024-03-04 15:55:08 +00:00
for ( const Pair < StringName , BBVariable > & p : var_list ) {
2024-08-15 20:15:53 +00:00
if ( p_blackboard - > has_local_var ( p . first ) & & ! overwrite ) {
2024-08-15 22:44:37 +00:00
# ifdef DEBUG_ENABLED
2024-08-15 20:44:10 +00:00
Variant : : Type existing_type = p_blackboard - > get_var ( p . first ) . get_type ( ) ;
Variant : : Type planned_type = p . second . get_type ( ) ;
2024-08-15 22:44:37 +00:00
if ( existing_type ! = planned_type & & existing_type ! = Variant : : NIL & & planned_type ! = Variant : : NIL & & ! ( existing_type = = Variant : : OBJECT & & planned_type = = Variant : : NODE_PATH ) ) {
2024-08-15 20:44:10 +00:00
WARN_PRINT ( vformat ( " BlackboardPlan: Not overwriting %s as it already exists in the blackboard, but it has a different type than planned (%s vs %s). File: %s " ,
LimboUtility : : get_singleton ( ) - > decorate_var ( p . first ) , Variant : : get_type_name ( existing_type ) , Variant : : get_type_name ( planned_type ) , get_path ( ) ) ) ;
}
2024-08-15 22:44:37 +00:00
# endif
2024-03-13 21:00:50 +00:00
continue ;
2024-01-23 11:05:54 +00:00
}
2024-11-11 09:10:20 +00:00
bool is_bound = has_property_binding ( p . first ) | | ( is_derived ( ) & & get_base_plan ( ) - > has_property_binding ( p . first ) ) ;
2024-08-14 18:07:42 +00:00
bool has_mapping = parent_scope_mapping . has ( p . first ) ;
2024-11-10 18:35:49 +00:00
bool do_prefetch = ! is_bound & & ! has_mapping & & prefetch_nodepath_vars ;
2024-09-15 12:16:54 +00:00
// Add a variable duplicate to the blackboard, optionally with NodePath prefetch.
BBVariable var = p . second . duplicate ( true ) ;
if ( unlikely ( do_prefetch & & p . second . get_type ( ) = = Variant : : NODE_PATH ) ) {
2024-09-15 12:29:23 +00:00
Node * prefetch_root = ! p_prefetch_root_for_base_plan | | ! is_derived ( ) | | is_derived_var_changed ( p . first ) ? p_prefetch_root : p_prefetch_root_for_base_plan ;
2024-09-15 12:16:54 +00:00
Node * n = prefetch_root - > get_node_or_null ( p . second . get_value ( ) ) ;
if ( n ! = nullptr ) {
var . set_value ( n ) ;
} else {
ERR_PRINT ( vformat ( " BlackboardPlan: Prefetch failed for variable $%s with value: %s " , p . first , p . second . get_value ( ) ) ) ;
var . set_value ( Variant ( ) ) ;
}
}
p_blackboard - > assign_var ( p . first , var ) ;
2024-08-14 18:07:42 +00:00
if ( has_mapping ) {
2024-05-13 21:21:55 +00:00
StringName target_var = parent_scope_mapping [ p . first ] ;
if ( target_var ! = StringName ( ) ) {
2024-11-03 03:53:56 +00:00
ERR_CONTINUE_MSG ( p_blackboard - > get_parent ( ) . is_null ( ) , vformat ( " BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set. " , LimboUtility : : get_singleton ( ) - > decorate_var ( p . first ) ) ) ;
2024-05-13 21:21:55 +00:00
p_blackboard - > link_var ( p . first , p_blackboard - > get_parent ( ) , target_var ) ;
}
2024-11-10 18:35:49 +00:00
} else if ( is_bound ) {
// Bind variable to a property of a scene node.
2024-11-11 09:10:20 +00:00
NodePath binding_path ;
Node * binding_root ;
if ( has_property_binding ( p . first ) ) {
binding_path = property_bindings [ p . first ] ;
binding_root = p_prefetch_root ;
} else {
binding_path = get_base_plan ( ) - > property_bindings [ p . first ] ;
binding_root = p_prefetch_root_for_base_plan ;
}
2024-11-10 18:35:49 +00:00
ERR_CONTINUE_MSG ( binding_path . get_subname_count ( ) ! = 1 , vformat ( " BlackboardPlan: Can't bind variable %s using property path that contains multiple sub-names: %s " , LimboUtility : : get_singleton ( ) - > decorate_var ( p . first ) , binding_path ) ) ;
NodePath node_path { binding_path . get_concatenated_names ( ) } ;
StringName prop_name = binding_path . get_subname ( 0 ) ;
// TODO: Implement binding for base plan as well.
Node * n = binding_root - > get_node_or_null ( node_path ) ;
ERR_CONTINUE_MSG ( n = = nullptr , vformat ( " BlackboardPlan: Binding failed for variable %s using property path: %s " , LimboUtility : : get_singleton ( ) - > decorate_var ( p . first ) , binding_path ) ) ;
var . bind ( n , prop_name ) ;
2024-05-13 21:21:55 +00:00
}
2024-01-23 11:05:54 +00:00
}
}
2024-01-23 14:31:56 +00:00
2024-03-02 15:06:32 +00:00
void BlackboardPlan : : _bind_methods ( ) {
2024-03-06 19:17:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_prefetch_nodepath_vars " , " enable " ) , & BlackboardPlan : : set_prefetch_nodepath_vars ) ;
ClassDB : : bind_method ( D_METHOD ( " is_prefetching_nodepath_vars " ) , & BlackboardPlan : : is_prefetching_nodepath_vars ) ;
2024-03-08 14:33:28 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_base_plan " , " blackboard_plan " ) , & BlackboardPlan : : set_base_plan ) ;
ClassDB : : bind_method ( D_METHOD ( " get_base_plan " ) , & BlackboardPlan : : get_base_plan ) ;
ClassDB : : bind_method ( D_METHOD ( " is_derived " ) , & BlackboardPlan : : is_derived ) ;
ClassDB : : bind_method ( D_METHOD ( " sync_with_base_plan " ) , & BlackboardPlan : : sync_with_base_plan ) ;
2024-05-15 18:43:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_parent_scope_plan_provider " , " callable " ) , & BlackboardPlan : : set_parent_scope_plan_provider ) ;
ClassDB : : bind_method ( D_METHOD ( " get_parent_scope_plan_provider " ) , & BlackboardPlan : : get_parent_scope_plan_provider ) ;
2024-09-15 12:16:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " create_blackboard " , " prefetch_root " , " parent_scope " , " prefetch_root_for_base_plan " ) , & BlackboardPlan : : create_blackboard , DEFVAL ( Ref < Blackboard > ( ) ) , DEFVAL ( Variant ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " populate_blackboard " , " blackboard " , " overwrite " , " prefetch_root " , " prefetch_root_for_base_plan " ) , & BlackboardPlan : : populate_blackboard , DEFVAL ( Variant ( ) ) ) ;
2024-03-06 19:17:23 +00:00
2024-03-08 14:33:28 +00:00
// To avoid cluttering the member namespace, we do not export unnecessary properties in this class.
2024-03-06 19:17:23 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " prefetch_nodepath_vars " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_STORAGE ) , " set_prefetch_nodepath_vars " , " is_prefetching_nodepath_vars " ) ;
2024-03-02 15:06:32 +00:00
}
2024-01-23 19:02:23 +00:00
BlackboardPlan : : BlackboardPlan ( ) {
2024-01-23 14:31:56 +00:00
}