2022-03-15 13:29:32 +01:00
/*************************************************************************/
/* node.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
# include "node.h"
# include "core/core_string_names.h"
# include "core/io/resource_loader.h"
2022-08-17 13:45:14 +02:00
# include "core/object/message_queue.h"
2022-08-17 14:19:55 +02:00
# include "core/string/print_string.h"
2022-03-15 13:29:32 +01:00
# include "instance_placeholder.h"
2022-07-27 13:37:17 +02:00
# include "scene/animation/scene_tree_tween.h"
2022-03-15 13:29:32 +01:00
# include "scene/resources/packed_scene.h"
# include "scene/scene_string_names.h"
# include "viewport.h"
2023-04-10 15:55:34 +02:00
# include "world.h"
2022-03-15 13:29:32 +01:00
2022-08-17 15:35:28 +02:00
# include "core/config/project_settings.h"
Ported from Godot4: New and improved IK system for Skeleton2D
This PR and commit adds a new IK system for 2D with the Skeleton2D node
that adds several new IK solvers, a way to control bones in a Skeleton2D
node similar to that in Skeleton3D. It also adds additional changes
and functionality.
This work was sponsored by GSoC 2020 and TwistedTwigleg.
Full list of changes:
* Adds a SkeletonModifier2D resource
* This resource is the base where all IK code is written and executed
* Has a function for clamping angles, since it is so commonly used
* Modifiers are unique when duplicated so it works with instancing
* Adds a SkeletonModifierStack2D resource
* This resource manages a series of SkeletonModification2Ds
* This is what the Skeleton2D directly interfaces with to make IK possible
* Adds SkeletonModifier2D resources for LookAt, CCDIK, FABRIK, Jiggle, and TwoBoneIK
* Each modification is in its own file
* There is also a SkeletonModifier2D resource that acts as a stack for using multiple stacks together
* Adds a PhysicalBone2D node
* Works similar to the PhysicalBone3D node, but uses a RigidBody2D node
* Changes to Skeleton2D listed below:
* Skeleton2D now holds a single SkeletonModificationStack2D for IK
* Skeleton2D now has a local_pose_override, which overrides the Bone2D position similar to how the overrides work in Skeleton3D
* Changes to Bone2D listed below:
* The default_length property has been changed to length. Length is the length of the bone to its child bone node
* New bone_angle property, which is the angle the bone has to its first child bone node
* Bone2D caches its transform when not modified by IK for IK interpolation purposes
* Bone2D draws its own editor gizmo, though this is stated to change in the future
* Changes to CanvasItemEditor listed below:
* Bone2D gizmo drawing code removed
* The 2D IK code is removed. Now Bone2D is the only bone system for 2D
* Transform2D now has a looking_at function for rotating to face a position
* Two new node notifications: NOTIFICATION_EDITOR_PRE_SAVE and NOTIFICATION_EDITOR_POST_SAVE
* These notifications only are called in the editor right before and after saving a scene
* Needed for not saving the IK position when executing IK in the editor
* Documentation for all the changes listed above.
- TwistedTwigleg
https://github.com/godotengine/godot/commit/8aa3c2f0918707c2243b86ac53c02c27ceb9a266
2022-08-18 18:47:30 +02:00
# include "core/object/class_db.h"
2022-08-17 13:45:14 +02:00
# include "core/object/script_language.h"
2022-03-17 19:35:23 +01:00
2022-03-15 13:29:32 +01:00
# ifdef TOOLS_ENABLED
# include "editor/editor_settings.h"
# endif
VARIANT_ENUM_CAST ( Node : : PauseMode ) ;
2022-07-27 15:16:37 +02:00
VARIANT_ENUM_CAST ( Node : : PhysicsInterpolationMode ) ;
2022-03-15 13:29:32 +01:00
int Node : : orphan_node_count = 0 ;
void Node : : _notification ( int p_notification ) {
switch ( p_notification ) {
case NOTIFICATION_PROCESS : {
if ( get_script_instance ( ) ) {
Variant time = get_process_delta_time ( ) ;
const Variant * ptr [ 1 ] = { & time } ;
get_script_instance ( ) - > call_multilevel ( SceneStringNames : : get_singleton ( ) - > _process , ptr , 1 ) ;
}
} break ;
case NOTIFICATION_PHYSICS_PROCESS : {
if ( get_script_instance ( ) ) {
Variant time = get_physics_process_delta_time ( ) ;
const Variant * ptr [ 1 ] = { & time } ;
get_script_instance ( ) - > call_multilevel ( SceneStringNames : : get_singleton ( ) - > _physics_process , ptr , 1 ) ;
}
} break ;
case NOTIFICATION_ENTER_TREE : {
ERR_FAIL_COND ( ! get_viewport ( ) ) ;
2022-08-29 19:40:17 +02:00
ERR_FAIL_COND ( ! get_world ( ) ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND ( ! get_tree ( ) ) ;
if ( data . pause_mode = = PAUSE_MODE_INHERIT ) {
if ( data . parent ) {
data . pause_owner = data . parent - > data . pause_owner ;
} else {
data . pause_owner = nullptr ;
}
} else {
data . pause_owner = this ;
}
2022-07-27 15:16:37 +02:00
if ( data . physics_interpolation_mode = = PHYSICS_INTERPOLATION_MODE_INHERIT ) {
bool interpolate = true ; // Root node default is for interpolation to be on
if ( data . parent ) {
interpolate = data . parent - > is_physics_interpolated ( ) ;
}
_propagate_physics_interpolated ( interpolate ) ;
}
2022-03-15 13:29:32 +01:00
if ( data . input ) {
add_to_group ( " _vp_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
if ( data . unhandled_input ) {
add_to_group ( " _vp_unhandled_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
if ( data . unhandled_key_input ) {
add_to_group ( " _vp_unhandled_key_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
get_tree ( ) - > node_count + + ;
orphan_node_count - - ;
} break ;
case NOTIFICATION_EXIT_TREE : {
ERR_FAIL_COND ( ! get_viewport ( ) ) ;
2022-08-29 19:40:17 +02:00
ERR_FAIL_COND ( ! get_world ( ) ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND ( ! get_tree ( ) ) ;
get_tree ( ) - > node_count - - ;
orphan_node_count + + ;
2022-08-19 00:04:38 +02:00
for ( int i = 0 ; i < data . _seen_by . size ( ) ; + + i ) {
Node * n = data . _seen_by . get ( i ) ;
if ( ObjectDB : : instance_validate ( n ) ) {
n - > sees_remove ( this ) ;
}
}
data . _seen_by . clear ( ) ;
data . _sees . clear ( ) ;
2022-03-15 13:29:32 +01:00
if ( data . input ) {
remove_from_group ( " _vp_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
if ( data . unhandled_input ) {
remove_from_group ( " _vp_unhandled_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
if ( data . unhandled_key_input ) {
remove_from_group ( " _vp_unhandled_key_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
data . pause_owner = nullptr ;
if ( data . path_cache ) {
memdelete ( data . path_cache ) ;
data . path_cache = nullptr ;
}
} break ;
case NOTIFICATION_PATH_CHANGED : {
if ( data . path_cache ) {
memdelete ( data . path_cache ) ;
data . path_cache = nullptr ;
}
} break ;
case NOTIFICATION_READY : {
if ( get_script_instance ( ) ) {
if ( get_script_instance ( ) - > has_method ( SceneStringNames : : get_singleton ( ) - > _input ) ) {
set_process_input ( true ) ;
}
if ( get_script_instance ( ) - > has_method ( SceneStringNames : : get_singleton ( ) - > _unhandled_input ) ) {
set_process_unhandled_input ( true ) ;
}
if ( get_script_instance ( ) - > has_method ( SceneStringNames : : get_singleton ( ) - > _unhandled_key_input ) ) {
set_process_unhandled_key_input ( true ) ;
}
if ( get_script_instance ( ) - > has_method ( SceneStringNames : : get_singleton ( ) - > _process ) ) {
set_process ( true ) ;
}
if ( get_script_instance ( ) - > has_method ( SceneStringNames : : get_singleton ( ) - > _physics_process ) ) {
set_physics_process ( true ) ;
}
get_script_instance ( ) - > call_multilevel_reversed ( SceneStringNames : : get_singleton ( ) - > _ready , nullptr , 0 ) ;
}
} break ;
case NOTIFICATION_POSTINITIALIZE : {
data . in_constructor = false ;
} break ;
case NOTIFICATION_PREDELETE : {
if ( data . parent ) {
data . parent - > remove_child ( this ) ;
}
// kill children as cleanly as possible
while ( data . children . size ( ) ) {
2023-04-10 17:32:42 +02:00
Node * child = data . children . back ( ) - > value ( ) ; // begin from the end because its faster and more consistent with creation
2022-03-15 13:29:32 +01:00
memdelete ( child ) ;
}
} break ;
}
}
void Node : : _propagate_ready ( ) {
data . ready_notified = true ;
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_ready ( ) ;
2022-03-15 13:29:32 +01:00
}
2023-04-10 17:32:42 +02:00
2022-03-15 13:29:32 +01:00
data . blocked - - ;
notification ( NOTIFICATION_POST_ENTER_TREE ) ;
if ( data . ready_first ) {
data . ready_first = false ;
notification ( NOTIFICATION_READY ) ;
emit_signal ( SceneStringNames : : get_singleton ( ) - > ready ) ;
}
}
void Node : : _propagate_physics_interpolated ( bool p_interpolated ) {
2022-07-27 15:16:37 +02:00
switch ( data . physics_interpolation_mode ) {
case PHYSICS_INTERPOLATION_MODE_INHERIT :
// keep the parent p_interpolated
break ;
case PHYSICS_INTERPOLATION_MODE_OFF : {
p_interpolated = false ;
} break ;
case PHYSICS_INTERPOLATION_MODE_ON : {
p_interpolated = true ;
} break ;
}
// no change? no need to propagate further
if ( data . physics_interpolated = = p_interpolated ) {
return ;
}
2022-03-15 13:29:32 +01:00
data . physics_interpolated = p_interpolated ;
2022-08-19 20:50:20 +02:00
// allow a call to the RenderingServer etc in derived classes
2022-03-15 13:29:32 +01:00
_physics_interpolated_changed ( ) ;
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_physics_interpolated ( p_interpolated ) ;
2022-03-15 13:29:32 +01:00
}
data . blocked - - ;
}
2022-07-27 17:51:39 +02:00
void Node : : _propagate_physics_interpolation_reset_requested ( ) {
if ( is_physics_interpolated ( ) ) {
data . physics_interpolation_reset_requested = true ;
}
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_physics_interpolation_reset_requested ( ) ;
2022-07-27 17:51:39 +02:00
}
data . blocked - - ;
}
2022-03-15 13:29:32 +01:00
void Node : : _propagate_enter_tree ( ) {
// this needs to happen to all children before any enter_tree
if ( data . parent ) {
data . tree = data . parent - > data . tree ;
data . depth = data . parent - > data . depth + 1 ;
} else {
data . depth = 1 ;
}
data . viewport = Object : : cast_to < Viewport > ( this ) ;
if ( ! data . viewport & & data . parent ) {
data . viewport = data . parent - > data . viewport ;
}
2022-08-29 19:40:17 +02:00
data . world = Object : : cast_to < World > ( this ) ;
if ( ! data . world & & data . parent ) {
data . world = data . parent - > data . world ;
}
2022-03-15 13:29:32 +01:00
data . inside_tree = true ;
2023-01-15 19:12:50 +01:00
for ( RBMap < StringName , GroupData > : : Element * E = data . grouped . front ( ) ; E ; E = E - > next ( ) ) {
2022-03-15 13:29:32 +01:00
E - > get ( ) . group = data . tree - > add_to_group ( E - > key ( ) , this ) ;
}
notification ( NOTIFICATION_ENTER_TREE ) ;
if ( get_script_instance ( ) ) {
get_script_instance ( ) - > call_multilevel_reversed ( SceneStringNames : : get_singleton ( ) - > _enter_tree , nullptr , 0 ) ;
}
emit_signal ( SceneStringNames : : get_singleton ( ) - > tree_entered ) ;
data . tree - > node_added ( this ) ;
if ( data . parent ) {
Variant c = this ;
const Variant * cptr = & c ;
data . parent - > emit_signal ( SceneStringNames : : get_singleton ( ) - > child_entered_tree , & cptr , 1 ) ;
}
data . blocked + + ;
//block while adding children
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
if ( ! E - > value ( ) - > is_inside_tree ( ) ) { // could have been added in enter_tree
E - > value ( ) - > _propagate_enter_tree ( ) ;
2022-03-15 13:29:32 +01:00
}
}
data . blocked - - ;
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) & & data . filename ! = String ( ) ) {
//used for live edit
data . tree - > live_scene_edit_cache [ data . filename ] . insert ( this ) ;
}
# endif
// enter groups
}
void Node : : _propagate_after_exit_branch ( bool p_exiting_tree ) {
// Clear owner if it was not part of the pruned branch
if ( data . owner ) {
bool found = false ;
Node * parent = data . parent ;
while ( parent ) {
if ( parent = = data . owner ) {
found = true ;
break ;
}
parent = parent - > data . parent ;
}
if ( ! found ) {
2022-07-27 00:45:48 +02:00
if ( data . unique_name_in_owner ) {
_release_unique_name_in_owner ( ) ;
}
2022-03-15 13:29:32 +01:00
data . owner - > data . owned . erase ( data . OW ) ;
data . owner = nullptr ;
}
}
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_after_exit_branch ( p_exiting_tree ) ;
2022-03-15 13:29:32 +01:00
}
data . blocked - - ;
if ( p_exiting_tree ) {
emit_signal ( SceneStringNames : : get_singleton ( ) - > tree_exited ) ;
}
}
void Node : : _propagate_exit_tree ( ) {
//block while removing children
# ifdef DEBUG_ENABLED
if ( ScriptDebugger : : get_singleton ( ) & & data . filename ! = String ( ) ) {
//used for live edit
2023-01-15 19:42:08 +01:00
RBMap < String , RBSet < Node * > > : : Element * E = data . tree - > live_scene_edit_cache . find ( data . filename ) ;
2022-03-15 13:29:32 +01:00
if ( E ) {
E - > get ( ) . erase ( this ) ;
if ( E - > get ( ) . size ( ) = = 0 ) {
data . tree - > live_scene_edit_cache . erase ( E ) ;
}
}
2023-01-15 19:12:50 +01:00
RBMap < Node * , RBMap < ObjectID , Node * > > : : Element * F = data . tree - > live_edit_remove_list . find ( this ) ;
2022-03-15 13:29:32 +01:00
if ( F ) {
2023-01-15 19:12:50 +01:00
for ( RBMap < ObjectID , Node * > : : Element * G = F - > get ( ) . front ( ) ; G ; G = G - > next ( ) ) {
2022-03-15 13:29:32 +01:00
memdelete ( G - > get ( ) ) ;
}
data . tree - > live_edit_remove_list . erase ( F ) ;
}
}
# endif
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . back ( ) ; E ; E = E - > prev ) {
E - > value ( ) - > _propagate_exit_tree ( ) ;
2022-03-15 13:29:32 +01:00
}
data . blocked - - ;
if ( get_script_instance ( ) ) {
get_script_instance ( ) - > call_multilevel ( SceneStringNames : : get_singleton ( ) - > _exit_tree , nullptr , 0 ) ;
}
emit_signal ( SceneStringNames : : get_singleton ( ) - > tree_exiting ) ;
notification ( NOTIFICATION_EXIT_TREE , true ) ;
if ( data . tree ) {
data . tree - > node_removed ( this ) ;
}
if ( data . parent ) {
Variant c = this ;
const Variant * cptr = & c ;
2022-06-20 09:53:44 +02:00
data . parent - > emit_signal ( SceneStringNames : : get_singleton ( ) - > child_exiting_tree , & cptr , 1 ) ;
2022-03-15 13:29:32 +01:00
}
// exit groups
2023-01-15 19:12:50 +01:00
for ( RBMap < StringName , GroupData > : : Element * E = data . grouped . front ( ) ; E ; E = E - > next ( ) ) {
2022-03-15 13:29:32 +01:00
data . tree - > remove_from_group ( E - > key ( ) , this ) ;
E - > get ( ) . group = nullptr ;
}
data . viewport = nullptr ;
2022-08-29 19:40:17 +02:00
data . world = nullptr ;
2022-03-15 13:29:32 +01:00
if ( data . tree ) {
data . tree - > tree_changed ( ) ;
}
data . inside_tree = false ;
data . ready_notified = false ;
data . tree = nullptr ;
data . depth = - 1 ;
}
void Node : : move_child ( Node * p_child , int p_pos ) {
ERR_FAIL_NULL ( p_child ) ;
2023-04-10 17:32:42 +02:00
ERR_FAIL_INDEX_MSG ( static_cast < uint32_t > ( p_pos ) , data . children . size ( ) + 1 , vformat ( " Invalid new child position: %d. " , p_pos ) ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND_MSG ( p_child - > data . parent ! = this , " Child is not a child of this node. " ) ;
ERR_FAIL_COND_MSG ( data . blocked > 0 , " Parent node is busy setting up children, move_child() failed. Consider using call_deferred( \" move_child \" ) instead (or \" popup \" if this is from a popup). " ) ;
2023-04-10 17:32:42 +02:00
_update_children_cache ( ) ;
2022-03-15 13:29:32 +01:00
// Specifying one place beyond the end
// means the same as moving to the last position
2023-04-10 17:32:42 +02:00
if ( static_cast < uint32_t > ( p_pos ) = = data . children . size ( ) ) {
2022-03-15 13:29:32 +01:00
p_pos - - ;
}
if ( p_child - > data . pos = = p_pos ) {
return ; //do nothing
}
int motion_from = MIN ( p_pos , p_child - > data . pos ) ;
int motion_to = MAX ( p_pos , p_child - > data . pos ) ;
2023-04-10 17:32:42 +02:00
data . children_cache . remove ( p_child - > data . pos ) ;
data . children_cache . insert ( p_pos , p_child ) ;
2022-03-15 13:29:32 +01:00
if ( data . tree ) {
data . tree - > tree_changed ( ) ;
}
data . blocked + + ;
//new pos first
for ( int i = motion_from ; i < = motion_to ; i + + ) {
2023-04-10 17:32:42 +02:00
data . children_cache [ i ] - > data . pos = i ;
2022-03-15 13:29:32 +01:00
}
// notification second
move_child_notify ( p_child ) ;
2023-04-10 15:55:34 +02:00
notification ( NOTIFICATION_CHILD_ORDER_CHANGED ) ;
emit_signal ( SceneStringNames : : get_singleton ( ) - > child_order_changed ) ;
2022-08-07 15:32:28 +02:00
p_child - > _propagate_groups_dirty ( ) ;
2022-03-15 13:29:32 +01:00
data . blocked - - ;
}
void Node : : raise ( ) {
if ( ! data . parent ) {
return ;
}
data . parent - > move_child ( this , data . parent - > data . children . size ( ) - 1 ) ;
}
void Node : : add_child_notify ( Node * p_child ) {
// to be used when not wanted
}
void Node : : remove_child_notify ( Node * p_child ) {
// to be used when not wanted
}
void Node : : move_child_notify ( Node * p_child ) {
// to be used when not wanted
}
2022-07-29 19:28:33 +02:00
void Node : : owner_changed_notify ( ) {
}
2022-03-15 13:29:32 +01:00
void Node : : _physics_interpolated_changed ( ) { }
void Node : : set_physics_process ( bool p_process ) {
if ( data . physics_process = = p_process ) {
return ;
}
data . physics_process = p_process ;
if ( data . physics_process ) {
add_to_group ( " physics_process " , false ) ;
} else {
remove_from_group ( " physics_process " ) ;
}
_change_notify ( " physics_process " ) ;
}
bool Node : : is_physics_processing ( ) const {
return data . physics_process ;
}
void Node : : set_physics_process_internal ( bool p_process_internal ) {
if ( data . physics_process_internal = = p_process_internal ) {
return ;
}
data . physics_process_internal = p_process_internal ;
if ( data . physics_process_internal ) {
add_to_group ( " physics_process_internal " , false ) ;
} else {
remove_from_group ( " physics_process_internal " ) ;
}
_change_notify ( " physics_process_internal " ) ;
}
bool Node : : is_physics_processing_internal ( ) const {
return data . physics_process_internal ;
}
void Node : : set_pause_mode ( PauseMode p_mode ) {
if ( data . pause_mode = = p_mode ) {
return ;
}
bool prev_inherits = data . pause_mode = = PAUSE_MODE_INHERIT ;
data . pause_mode = p_mode ;
if ( ! is_inside_tree ( ) ) {
return ; //pointless
}
if ( ( data . pause_mode = = PAUSE_MODE_INHERIT ) = = prev_inherits ) {
return ; ///nothing changed
}
Node * owner = nullptr ;
if ( data . pause_mode = = PAUSE_MODE_INHERIT ) {
if ( data . parent ) {
owner = data . parent - > data . pause_owner ;
}
} else {
owner = this ;
}
_propagate_pause_owner ( owner ) ;
}
Node : : PauseMode Node : : get_pause_mode ( ) const {
return data . pause_mode ;
}
void Node : : _propagate_pause_owner ( Node * p_owner ) {
if ( this ! = p_owner & & data . pause_mode ! = PAUSE_MODE_INHERIT ) {
return ;
}
data . pause_owner = p_owner ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_pause_owner ( p_owner ) ;
2022-03-15 13:29:32 +01:00
}
}
2022-08-07 15:32:28 +02:00
void Node : : _propagate_groups_dirty ( ) {
2023-01-15 19:12:50 +01:00
for ( const RBMap < StringName , GroupData > : : Element * E = data . grouped . front ( ) ; E ; E = E - > next ( ) ) {
2022-08-07 15:32:28 +02:00
if ( E - > get ( ) . group ) {
E - > get ( ) . group - > changed = true ;
}
}
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_groups_dirty ( ) ;
2022-08-07 15:32:28 +02:00
}
}
2022-08-19 00:04:38 +02:00
Node * Node : : sees_get ( int p_index ) {
ERR_FAIL_INDEX_V ( p_index , data . _sees . size ( ) , NULL ) ;
return data . _sees . get ( p_index ) ;
}
void Node : : sees_remove_index ( int p_index ) {
ERR_FAIL_INDEX ( p_index , data . _sees . size ( ) ) ;
Node * e = data . _sees . get ( p_index ) ;
if ( unlikely ( ! ObjectDB : : instance_validate ( e ) ) ) {
data . _sees . remove ( p_index ) ;
return ;
}
e - > seen_by_remove ( this ) ;
data . _sees . remove ( p_index ) ;
}
void Node : : sees_remove ( Node * p_node ) {
if ( unlikely ( ! ObjectDB : : instance_validate ( p_node ) ) ) {
data . _sees . erase ( p_node ) ;
return ;
}
p_node - > seen_by_remove ( this ) ;
data . _sees . erase ( p_node ) ;
}
void Node : : sees_add ( Node * p_node ) {
ERR_FAIL_COND ( ! ObjectDB : : instance_validate ( p_node ) ) ;
p_node - > seen_by_add ( this ) ;
for ( int i = 0 ; i < data . _sees . size ( ) ; + + i ) {
2023-04-10 17:32:42 +02:00
if ( data . _sees . get ( i ) = = p_node ) {
2022-08-19 00:04:38 +02:00
return ;
2023-04-10 17:32:42 +02:00
}
2022-08-19 00:04:38 +02:00
}
data . _sees . push_back ( p_node ) ;
}
int Node : : sees_get_count ( ) {
return data . _sees . size ( ) ;
}
Node * Node : : seen_by_get ( int p_index ) {
ERR_FAIL_INDEX_V ( p_index , data . _seen_by . size ( ) , NULL ) ;
return data . _seen_by . get ( p_index ) ;
}
void Node : : seen_by_remove_index ( int p_index ) {
ERR_FAIL_INDEX ( p_index , data . _sees . size ( ) ) ;
data . _seen_by . remove ( p_index ) ;
}
void Node : : seen_by_remove ( Node * p_node ) {
data . _seen_by . erase ( p_node ) ;
}
void Node : : seen_by_add ( Node * p_node ) {
ERR_FAIL_COND ( ! ObjectDB : : instance_validate ( p_node ) ) ;
for ( int i = 0 ; i < data . _seen_by . size ( ) ; + + i ) {
2023-04-10 17:32:42 +02:00
if ( data . _seen_by . get ( i ) = = p_node ) {
2022-08-19 00:04:38 +02:00
return ;
2023-04-10 17:32:42 +02:00
}
2022-08-19 00:04:38 +02:00
}
data . _seen_by . push_back ( p_node ) ;
}
int Node : : seen_by_get_count ( ) {
return data . _seen_by . size ( ) ;
}
2022-03-15 13:29:32 +01:00
void Node : : set_network_master ( int p_peer_id , bool p_recursive ) {
data . network_master = p_peer_id ;
if ( p_recursive ) {
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > set_network_master ( p_peer_id , true ) ;
2022-03-15 13:29:32 +01:00
}
}
}
int Node : : get_network_master ( ) const {
return data . network_master ;
}
bool Node : : is_network_master ( ) const {
ERR_FAIL_COND_V ( ! is_inside_tree ( ) , false ) ;
return get_multiplayer ( ) - > get_network_unique_id ( ) = = data . network_master ;
}
/***** RPC CONFIG ********/
void Node : : rpc_config ( const StringName & p_method , MultiplayerAPI : : RPCMode p_mode ) {
if ( p_mode = = MultiplayerAPI : : RPC_MODE_DISABLED ) {
data . rpc_methods . erase ( p_method ) ;
} else {
data . rpc_methods [ p_method ] = p_mode ;
} ;
}
/***** RPC FUNCTIONS ********/
void Node : : rpc ( const StringName & p_method , VARIANT_ARG_DECLARE ) {
VARIANT_ARGPTRS ;
int argc = 0 ;
for ( int i = 0 ; i < VARIANT_ARG_MAX ; i + + ) {
if ( argptr [ i ] - > get_type ( ) = = Variant : : NIL ) {
break ;
}
argc + + ;
}
rpcp ( 0 , false , p_method , argptr , argc ) ;
}
void Node : : rpc_id ( int p_peer_id , const StringName & p_method , VARIANT_ARG_DECLARE ) {
VARIANT_ARGPTRS ;
int argc = 0 ;
for ( int i = 0 ; i < VARIANT_ARG_MAX ; i + + ) {
if ( argptr [ i ] - > get_type ( ) = = Variant : : NIL ) {
break ;
}
argc + + ;
}
rpcp ( p_peer_id , false , p_method , argptr , argc ) ;
}
void Node : : rpc_unreliable ( const StringName & p_method , VARIANT_ARG_DECLARE ) {
VARIANT_ARGPTRS ;
int argc = 0 ;
for ( int i = 0 ; i < VARIANT_ARG_MAX ; i + + ) {
if ( argptr [ i ] - > get_type ( ) = = Variant : : NIL ) {
break ;
}
argc + + ;
}
rpcp ( 0 , true , p_method , argptr , argc ) ;
}
void Node : : rpc_unreliable_id ( int p_peer_id , const StringName & p_method , VARIANT_ARG_DECLARE ) {
VARIANT_ARGPTRS ;
int argc = 0 ;
for ( int i = 0 ; i < VARIANT_ARG_MAX ; i + + ) {
if ( argptr [ i ] - > get_type ( ) = = Variant : : NIL ) {
break ;
}
argc + + ;
}
rpcp ( p_peer_id , true , p_method , argptr , argc ) ;
}
2022-08-19 00:04:38 +02:00
void Node : : vrpc ( const StringName & p_method , VARIANT_ARG_DECLARE ) {
VARIANT_ARGPTRS ;
int argc = 0 ;
for ( int i = 0 ; i < VARIANT_ARG_MAX ; i + + ) {
2023-04-10 17:32:42 +02:00
if ( argptr [ i ] - > get_type ( ) = = Variant : : NIL ) {
2022-08-19 00:04:38 +02:00
break ;
2023-04-10 17:32:42 +02:00
}
2022-08-19 00:04:38 +02:00
argc + + ;
}
for ( int i = 0 ; i < data . _seen_by . size ( ) ; + + i ) {
Node * e = data . _seen_by . get ( i ) ;
if ( unlikely ( ! ObjectDB : : instance_validate ( e ) ) ) {
data . _seen_by . remove ( i ) ;
- - i ;
continue ;
}
int netm = e - > get_network_master ( ) ;
if ( netm ! = 1 ) {
rpcp ( netm , false , p_method , argptr , argc ) ;
}
}
if ( get_network_master ( ) ! = 1 ) {
rpcp ( get_network_master ( ) , false , p_method , argptr , argc ) ;
}
}
void Node : : vrpc_unreliable ( const StringName & p_method , VARIANT_ARG_DECLARE ) {
VARIANT_ARGPTRS ;
int argc = 0 ;
for ( int i = 0 ; i < VARIANT_ARG_MAX ; i + + ) {
2023-04-10 17:32:42 +02:00
if ( argptr [ i ] - > get_type ( ) = = Variant : : NIL ) {
2022-08-19 00:04:38 +02:00
break ;
2023-04-10 17:32:42 +02:00
}
2022-08-19 00:04:38 +02:00
argc + + ;
}
for ( int i = 0 ; i < data . _seen_by . size ( ) ; + + i ) {
Node * e = data . _seen_by . get ( i ) ;
if ( unlikely ( ! ObjectDB : : instance_validate ( e ) ) ) {
data . _seen_by . remove ( i ) ;
- - i ;
continue ;
}
int netm = e - > get_network_master ( ) ;
if ( netm ! = 1 ) {
rpcp ( netm , true , p_method , argptr , argc ) ;
}
}
if ( get_network_master ( ) ! = 1 ) {
rpcp ( get_network_master ( ) , true , p_method , argptr , argc ) ;
}
}
2022-03-15 13:29:32 +01:00
Variant Node : : _rpc_bind ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
if ( p_argcount < 1 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 1 ;
return Variant ( ) ;
}
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 0 ;
r_error . expected = Variant : : STRING ;
return Variant ( ) ;
}
StringName method = * p_args [ 0 ] ;
rpcp ( 0 , false , method , & p_args [ 1 ] , p_argcount - 1 ) ;
r_error . error = Variant : : CallError : : CALL_OK ;
return Variant ( ) ;
}
Variant Node : : _rpc_id_bind ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
if ( p_argcount < 2 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 2 ;
return Variant ( ) ;
}
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : INT ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 0 ;
r_error . expected = Variant : : INT ;
return Variant ( ) ;
}
if ( p_args [ 1 ] - > get_type ( ) ! = Variant : : STRING ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 1 ;
r_error . expected = Variant : : STRING ;
return Variant ( ) ;
}
int peer_id = * p_args [ 0 ] ;
StringName method = * p_args [ 1 ] ;
rpcp ( peer_id , false , method , & p_args [ 2 ] , p_argcount - 2 ) ;
r_error . error = Variant : : CallError : : CALL_OK ;
return Variant ( ) ;
}
Variant Node : : _rpc_unreliable_bind ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
if ( p_argcount < 1 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 1 ;
return Variant ( ) ;
}
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 0 ;
r_error . expected = Variant : : STRING ;
return Variant ( ) ;
}
StringName method = * p_args [ 0 ] ;
rpcp ( 0 , true , method , & p_args [ 1 ] , p_argcount - 1 ) ;
r_error . error = Variant : : CallError : : CALL_OK ;
return Variant ( ) ;
}
Variant Node : : _rpc_unreliable_id_bind ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
if ( p_argcount < 2 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 2 ;
return Variant ( ) ;
}
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : INT ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 0 ;
r_error . expected = Variant : : INT ;
return Variant ( ) ;
}
if ( p_args [ 1 ] - > get_type ( ) ! = Variant : : STRING ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 1 ;
r_error . expected = Variant : : STRING ;
return Variant ( ) ;
}
int peer_id = * p_args [ 0 ] ;
StringName method = * p_args [ 1 ] ;
rpcp ( peer_id , true , method , & p_args [ 2 ] , p_argcount - 2 ) ;
r_error . error = Variant : : CallError : : CALL_OK ;
return Variant ( ) ;
}
2022-08-19 00:04:38 +02:00
Variant Node : : _vrpc_bind ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
if ( p_argcount < 1 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 1 ;
return Variant ( ) ;
}
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 0 ;
r_error . expected = Variant : : STRING ;
return Variant ( ) ;
}
StringName method = * p_args [ 0 ] ;
for ( int i = 0 ; i < data . _seen_by . size ( ) ; + + i ) {
Node * e = data . _seen_by . get ( i ) ;
if ( unlikely ( ! ObjectDB : : instance_validate ( e ) ) ) {
data . _seen_by . remove ( i ) ;
- - i ;
continue ;
}
int netm = e - > get_network_master ( ) ;
if ( netm ! = 1 ) {
rpcp ( netm , false , method , & p_args [ 1 ] , p_argcount - 1 ) ;
}
}
//call(method, &p_args[1], p_argcount - 1);
r_error . error = Variant : : CallError : : CALL_OK ;
return Variant ( ) ;
}
Variant Node : : _vrpc_unreliable_bind ( const Variant * * p_args , int p_argcount , Variant : : CallError & r_error ) {
if ( p_argcount < 1 ) {
r_error . error = Variant : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS ;
r_error . argument = 1 ;
return Variant ( ) ;
}
if ( p_args [ 0 ] - > get_type ( ) ! = Variant : : STRING ) {
r_error . error = Variant : : CallError : : CALL_ERROR_INVALID_ARGUMENT ;
r_error . argument = 0 ;
r_error . expected = Variant : : STRING ;
return Variant ( ) ;
}
StringName method = * p_args [ 0 ] ;
for ( int i = 0 ; i < data . _seen_by . size ( ) ; + + i ) {
Node * e = data . _seen_by . get ( i ) ;
if ( unlikely ( ! ObjectDB : : instance_validate ( e ) ) ) {
data . _seen_by . remove ( i ) ;
- - i ;
continue ;
}
int netm = e - > get_network_master ( ) ;
if ( netm ! = 1 ) {
rpcp ( netm , true , method , & p_args [ 1 ] , p_argcount - 1 ) ;
}
}
//call(method, &p_args[1], p_argcount - 1);
r_error . error = Variant : : CallError : : CALL_OK ;
return Variant ( ) ;
}
2022-03-15 13:29:32 +01:00
void Node : : rpcp ( int p_peer_id , bool p_unreliable , const StringName & p_method , const Variant * * p_arg , int p_argcount ) {
ERR_FAIL_COND ( ! is_inside_tree ( ) ) ;
get_multiplayer ( ) - > rpcp ( this , p_peer_id , p_unreliable , p_method , p_arg , p_argcount ) ;
}
//////////// end of rpc
Ref < MultiplayerAPI > Node : : get_multiplayer ( ) const {
if ( multiplayer . is_valid ( ) ) {
return multiplayer ;
}
if ( ! is_inside_tree ( ) ) {
return Ref < MultiplayerAPI > ( ) ;
}
return get_tree ( ) - > get_multiplayer ( ) ;
}
Ref < MultiplayerAPI > Node : : get_custom_multiplayer ( ) const {
return multiplayer ;
}
void Node : : set_custom_multiplayer ( Ref < MultiplayerAPI > p_multiplayer ) {
multiplayer = p_multiplayer ;
}
2023-01-15 19:12:50 +01:00
const RBMap < StringName , MultiplayerAPI : : RPCMode > : : Element * Node : : get_node_rpc_mode ( const StringName & p_method ) {
2022-03-15 13:29:32 +01:00
return data . rpc_methods . find ( p_method ) ;
}
bool Node : : can_process_notification ( int p_what ) const {
switch ( p_what ) {
case NOTIFICATION_PHYSICS_PROCESS :
return data . physics_process ;
case NOTIFICATION_PROCESS :
return data . idle_process ;
case NOTIFICATION_INTERNAL_PROCESS :
return data . idle_process_internal ;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS :
return data . physics_process_internal ;
}
return true ;
}
bool Node : : can_process ( ) const {
ERR_FAIL_COND_V ( ! is_inside_tree ( ) , false ) ;
if ( get_tree ( ) - > is_paused ( ) ) {
if ( data . pause_mode = = PAUSE_MODE_STOP ) {
return false ;
}
if ( data . pause_mode = = PAUSE_MODE_PROCESS ) {
return true ;
}
if ( data . pause_mode = = PAUSE_MODE_INHERIT ) {
if ( ! data . pause_owner ) {
return false ; //clearly no pause owner by default
}
if ( data . pause_owner - > data . pause_mode = = PAUSE_MODE_PROCESS ) {
return true ;
}
if ( data . pause_owner - > data . pause_mode = = PAUSE_MODE_STOP ) {
return false ;
}
}
}
return true ;
}
2022-07-27 15:16:37 +02:00
void Node : : set_physics_interpolation_mode ( PhysicsInterpolationMode p_mode ) {
if ( data . physics_interpolation_mode = = p_mode ) {
return ;
}
data . physics_interpolation_mode = p_mode ;
bool interpolate = true ; // default for root node
switch ( p_mode ) {
case PHYSICS_INTERPOLATION_MODE_INHERIT : {
if ( is_inside_tree ( ) & & data . parent ) {
interpolate = data . parent - > is_physics_interpolated ( ) ;
}
} break ;
case PHYSICS_INTERPOLATION_MODE_OFF : {
interpolate = false ;
} break ;
case PHYSICS_INTERPOLATION_MODE_ON : {
interpolate = true ;
} break ;
}
2022-03-15 13:29:32 +01:00
// if swapping from interpolated to non-interpolated, use this as
// an extra means to cause a reset
2022-07-27 15:16:37 +02:00
if ( is_physics_interpolated ( ) & & ! interpolate ) {
2022-03-15 13:29:32 +01:00
reset_physics_interpolation ( ) ;
}
2022-07-27 15:16:37 +02:00
_propagate_physics_interpolated ( interpolate ) ;
2022-03-15 13:29:32 +01:00
}
void Node : : reset_physics_interpolation ( ) {
if ( is_physics_interpolated_and_enabled ( ) ) {
propagate_notification ( NOTIFICATION_RESET_PHYSICS_INTERPOLATION ) ;
}
}
float Node : : get_physics_process_delta_time ( ) const {
if ( data . tree ) {
return data . tree - > get_physics_process_time ( ) ;
} else {
return 0 ;
}
}
float Node : : get_process_delta_time ( ) const {
if ( data . tree ) {
return data . tree - > get_idle_process_time ( ) ;
} else {
return 0 ;
}
}
void Node : : set_process ( bool p_idle_process ) {
if ( data . idle_process = = p_idle_process ) {
return ;
}
data . idle_process = p_idle_process ;
if ( data . idle_process ) {
add_to_group ( " idle_process " , false ) ;
} else {
remove_from_group ( " idle_process " ) ;
}
_change_notify ( " idle_process " ) ;
}
bool Node : : is_processing ( ) const {
return data . idle_process ;
}
void Node : : set_process_internal ( bool p_idle_process_internal ) {
if ( data . idle_process_internal = = p_idle_process_internal ) {
return ;
}
data . idle_process_internal = p_idle_process_internal ;
if ( data . idle_process_internal ) {
add_to_group ( " idle_process_internal " , false ) ;
} else {
remove_from_group ( " idle_process_internal " ) ;
}
_change_notify ( " idle_process_internal " ) ;
}
bool Node : : is_processing_internal ( ) const {
return data . idle_process_internal ;
}
void Node : : set_process_priority ( int p_priority ) {
data . process_priority = p_priority ;
// Make sure we are in SceneTree.
if ( data . tree = = nullptr ) {
return ;
}
if ( is_processing ( ) ) {
data . tree - > make_group_changed ( " idle_process " ) ;
}
if ( is_processing_internal ( ) ) {
data . tree - > make_group_changed ( " idle_process_internal " ) ;
}
if ( is_physics_processing ( ) ) {
data . tree - > make_group_changed ( " physics_process " ) ;
}
if ( is_physics_processing_internal ( ) ) {
data . tree - > make_group_changed ( " physics_process_internal " ) ;
}
}
int Node : : get_process_priority ( ) const {
return data . process_priority ;
}
void Node : : set_process_input ( bool p_enable ) {
if ( p_enable = = data . input ) {
return ;
}
data . input = p_enable ;
if ( ! is_inside_tree ( ) ) {
return ;
}
if ( p_enable ) {
add_to_group ( " _vp_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
} else {
remove_from_group ( " _vp_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
}
bool Node : : is_processing_input ( ) const {
return data . input ;
}
void Node : : set_process_unhandled_input ( bool p_enable ) {
if ( p_enable = = data . unhandled_input ) {
return ;
}
data . unhandled_input = p_enable ;
if ( ! is_inside_tree ( ) ) {
return ;
}
if ( p_enable ) {
add_to_group ( " _vp_unhandled_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
} else {
remove_from_group ( " _vp_unhandled_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
}
bool Node : : is_processing_unhandled_input ( ) const {
return data . unhandled_input ;
}
void Node : : set_process_unhandled_key_input ( bool p_enable ) {
if ( p_enable = = data . unhandled_key_input ) {
return ;
}
data . unhandled_key_input = p_enable ;
if ( ! is_inside_tree ( ) ) {
return ;
}
if ( p_enable ) {
add_to_group ( " _vp_unhandled_key_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
} else {
remove_from_group ( " _vp_unhandled_key_input " + itos ( get_viewport ( ) - > get_instance_id ( ) ) ) ;
}
}
bool Node : : is_processing_unhandled_key_input ( ) const {
return data . unhandled_key_input ;
}
void Node : : _set_physics_interpolated_client_side ( bool p_enable ) {
data . physics_interpolated_client_side = p_enable ;
}
2022-07-27 17:51:39 +02:00
void Node : : _set_physics_interpolation_reset_requested ( bool p_enable ) {
data . physics_interpolation_reset_requested = p_enable ;
}
2022-03-15 13:29:32 +01:00
void Node : : _set_use_identity_transform ( bool p_enable ) {
data . use_identity_transform = p_enable ;
}
StringName Node : : get_name ( ) const {
return data . name ;
}
void Node : : _set_name_nocheck ( const StringName & p_name ) {
data . name = p_name ;
}
void Node : : set_name ( const String & p_name ) {
String name = p_name . validate_node_name ( ) ;
ERR_FAIL_COND ( name = = " " ) ;
2022-07-27 00:45:48 +02:00
if ( data . unique_name_in_owner & & data . owner ) {
_release_unique_name_in_owner ( ) ;
}
2023-04-10 17:32:42 +02:00
String old_name = data . name ;
2022-03-15 13:29:32 +01:00
data . name = name ;
if ( data . parent ) {
2023-04-10 17:32:42 +02:00
data . parent - > data . children . erase ( old_name ) ;
2022-03-15 13:29:32 +01:00
data . parent - > _validate_child_name ( this ) ;
2023-04-10 17:32:42 +02:00
data . parent - > data . children . insert ( data . name , this ) ;
2022-03-15 13:29:32 +01:00
}
2022-07-27 00:45:48 +02:00
if ( data . unique_name_in_owner & & data . owner ) {
_acquire_unique_name_in_owner ( ) ;
}
2022-03-15 13:29:32 +01:00
propagate_notification ( NOTIFICATION_PATH_CHANGED ) ;
if ( is_inside_tree ( ) ) {
emit_signal ( " renamed " ) ;
get_tree ( ) - > node_renamed ( this ) ;
get_tree ( ) - > tree_changed ( ) ;
}
}
static bool node_hrcr = false ;
static SafeRefCount node_hrcr_count ;
void Node : : init_node_hrcr ( ) {
node_hrcr_count . init ( 1 ) ;
}
void Node : : set_human_readable_collision_renaming ( bool p_enabled ) {
node_hrcr = p_enabled ;
}
# ifdef TOOLS_ENABLED
String Node : : validate_child_name ( Node * p_child ) {
StringName name = p_child - > data . name ;
_generate_serial_child_name ( p_child , name ) ;
return name ;
}
# endif
void Node : : _validate_child_name ( Node * p_child , bool p_force_human_readable ) {
/* Make sure the name is unique */
if ( node_hrcr | | p_force_human_readable ) {
//this approach to autoset node names is human readable but very slow
//it's turned on while running in the editor
StringName name = p_child - > data . name ;
_generate_serial_child_name ( p_child , name ) ;
p_child - > data . name = name ;
} else {
//this approach to autoset node names is fast but not as readable
//it's the default and reserves the '@' character for unique names.
bool unique = true ;
if ( p_child - > data . name = = StringName ( ) ) {
//new unique name must be assigned
unique = false ;
} else {
2023-04-10 17:32:42 +02:00
const Node * const * existing = data . children . getptr ( p_child - > data . name ) ;
unique = ! existing | | * existing = = p_child ;
2022-03-15 13:29:32 +01:00
}
if ( ! unique ) {
ERR_FAIL_COND ( ! node_hrcr_count . ref ( ) ) ;
2023-04-10 18:10:22 +02:00
// Optimized version of the code below:
// String name = "@" + String(p_child->get_name()) + "@" + itos(node_hrcr_count.get());
uint32_t c = node_hrcr_count . get ( ) ;
String cn = p_child - > get_class_name ( ) . operator String ( ) ;
const char32_t * cn_ptr = cn . ptr ( ) ;
uint32_t cn_length = cn . length ( ) ;
uint32_t c_chars = String : : num_characters ( c ) ;
uint32_t len = 2 + cn_length + c_chars ;
char32_t * str = ( char32_t * ) alloca ( sizeof ( char32_t ) * ( len + 1 ) ) ;
uint32_t idx = 0 ;
str [ idx + + ] = ' @ ' ;
for ( uint32_t i = 0 ; i < cn_length ; i + + ) {
str [ idx + + ] = cn_ptr [ i ] ;
}
str [ idx + + ] = ' @ ' ;
idx + = c_chars ;
ERR_FAIL_COND ( idx ! = len ) ;
str [ idx ] = 0 ;
while ( c ) {
str [ - - idx ] = ' 0 ' + ( c % 10 ) ;
c / = 10 ;
}
p_child - > data . name = String ( str ) ;
2022-03-15 13:29:32 +01:00
}
}
}
// Return s + 1 as if it were an integer
String increase_numeric_string ( const String & s ) {
String res = s ;
bool carry = res . length ( ) > 0 ;
for ( int i = res . length ( ) - 1 ; i > = 0 ; i - - ) {
if ( ! carry ) {
break ;
}
CharType n = s [ i ] ;
if ( n = = ' 9 ' ) { // keep carry as true: 9 + 1
res [ i ] = ' 0 ' ;
} else {
res [ i ] = s [ i ] + 1 ;
carry = false ;
}
}
if ( carry ) {
res = " 1 " + res ;
}
return res ;
}
void Node : : _generate_serial_child_name ( const Node * p_child , StringName & name ) const {
if ( name = = StringName ( ) ) {
//no name and a new nade is needed, create one.
name = p_child - > get_class ( ) ;
// Adjust casing according to project setting. The current type name is expected to be in PascalCase.
switch ( ProjectSettings : : get_singleton ( ) - > get ( " node/name_casing " ) . operator int ( ) ) {
case NAME_CASING_PASCAL_CASE :
break ;
case NAME_CASING_CAMEL_CASE : {
String n = name ;
n [ 0 ] = n . to_lower ( ) [ 0 ] ;
name = n ;
} break ;
case NAME_CASING_SNAKE_CASE :
name = String ( name ) . camelcase_to_underscore ( true ) ;
break ;
}
}
2023-04-10 17:32:42 +02:00
const Node * const * existing = data . children . getptr ( name ) ;
if ( ! existing | | * existing = = p_child ) { // Unused, or is current node.
return ;
2022-03-15 13:29:32 +01:00
}
// Extract trailing number
String name_string = name ;
String nums ;
for ( int i = name_string . length ( ) - 1 ; i > = 0 ; i - - ) {
CharType n = name_string [ i ] ;
if ( n > = ' 0 ' & & n < = ' 9 ' ) {
nums = String : : chr ( name_string [ i ] ) + nums ;
} else {
break ;
}
}
String nnsep = _get_name_num_separator ( ) ;
int name_last_index = name_string . length ( ) - nnsep . length ( ) - nums . length ( ) ;
// Assign the base name + separator to name if we have numbers preceded by a separator
if ( nums . length ( ) > 0 & & name_string . substr ( name_last_index , nnsep . length ( ) ) = = nnsep ) {
name_string = name_string . substr ( 0 , name_last_index + nnsep . length ( ) ) ;
} else {
nums = " " ;
}
for ( ; ; ) {
StringName attempt = name_string + nums ;
2023-04-10 17:32:42 +02:00
existing = data . children . getptr ( attempt ) ;
bool exists = existing ! = nullptr & & * existing ! = p_child ;
2022-03-15 13:29:32 +01:00
if ( ! exists ) {
name = attempt ;
return ;
} else {
if ( nums . length ( ) = = 0 ) {
// Name was undecorated so skip to 2 for a more natural result
nums = " 2 " ;
name_string + = nnsep ; // Add separator because nums.length() > 0 was false
} else {
nums = increase_numeric_string ( nums ) ;
}
}
}
}
void Node : : _add_child_nocheck ( Node * p_child , const StringName & p_name ) {
//add a child node quickly, without name validation
p_child - > data . name = p_name ;
p_child - > data . pos = data . children . size ( ) ;
2023-04-10 17:32:42 +02:00
data . children . insert ( p_name , p_child ) ;
2022-03-15 13:29:32 +01:00
p_child - > data . parent = this ;
2023-04-10 17:32:42 +02:00
if ( ! data . children_cache_dirty ) {
// Special case, also add to the cached children array since its cheap.
data . children_cache . push_back ( p_child ) ;
} else {
data . children_cache_dirty = true ;
}
2022-03-15 13:29:32 +01:00
p_child - > notification ( NOTIFICATION_PARENTED ) ;
if ( data . tree ) {
p_child - > _set_tree ( data . tree ) ;
}
/* Notify */
//recognize children created in this node constructor
p_child - > data . parent_owned = data . in_constructor ;
add_child_notify ( p_child ) ;
2023-04-10 15:55:34 +02:00
notification ( NOTIFICATION_CHILD_ORDER_CHANGED ) ;
emit_signal ( SceneStringNames : : get_singleton ( ) - > child_order_changed ) ;
2022-07-27 17:51:39 +02:00
// Allow physics interpolated nodes to automatically reset when added to the tree
// (this is to save the user doing this manually each time)
if ( is_inside_tree ( ) & & get_tree ( ) - > is_physics_interpolation_enabled ( ) ) {
p_child - > _propagate_physics_interpolation_reset_requested ( ) ;
}
2022-03-15 13:29:32 +01:00
}
2022-10-12 18:02:26 +02:00
void Node : : add_child ( Node * p_child , bool p_force_readable_name ) {
2022-03-15 13:29:32 +01:00
ERR_FAIL_NULL ( p_child ) ;
ERR_FAIL_COND_MSG ( p_child = = this , vformat ( " Can't add child '%s' to itself. " , p_child - > get_name ( ) ) ) ; // adding to itself!
ERR_FAIL_COND_MSG ( p_child - > data . parent , vformat ( " Can't add child '%s' to '%s', already has a parent '%s'. " , p_child - > get_name ( ) , get_name ( ) , p_child - > data . parent - > get_name ( ) ) ) ; //Fail if node has a parent
# ifdef DEBUG_ENABLED
ERR_FAIL_COND_MSG ( p_child - > is_a_parent_of ( this ) , vformat ( " Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'. " , p_child - > get_name ( ) , get_name ( ) , p_child - > get_name ( ) , get_name ( ) ) ) ;
# endif
ERR_FAIL_COND_MSG ( data . blocked > 0 , " Parent node is busy setting up children, add_node() failed. Consider using call_deferred( \" add_child \" , child) instead. " ) ;
/* Validate name */
2022-10-12 18:02:26 +02:00
_validate_child_name ( p_child , p_force_readable_name ) ;
2022-03-15 13:29:32 +01:00
_add_child_nocheck ( p_child , p_child - > data . name ) ;
}
2022-10-12 18:02:26 +02:00
void Node : : add_child_below_node ( Node * p_node , Node * p_child , bool p_force_readable_name ) {
2022-03-15 13:29:32 +01:00
ERR_FAIL_NULL ( p_node ) ;
ERR_FAIL_NULL ( p_child ) ;
2022-10-12 18:02:26 +02:00
add_child ( p_child , p_force_readable_name ) ;
2022-03-15 13:29:32 +01:00
if ( p_node - > data . parent = = this ) {
move_child ( p_child , p_node - > get_position_in_parent ( ) + 1 ) ;
} else {
WARN_PRINT ( " Cannot move under node " + p_node - > get_name ( ) + " as " + p_child - > get_name ( ) + " does not share a parent. " ) ;
}
}
void Node : : remove_child ( Node * p_child ) {
ERR_FAIL_NULL ( p_child ) ;
ERR_FAIL_COND_MSG ( data . blocked > 0 , " Parent node is busy setting up children, remove_node() failed. Consider using call_deferred( \" remove_child \" , child) instead. " ) ;
2023-04-10 17:32:42 +02:00
ERR_FAIL_COND ( p_child - > data . parent ! = this ) ;
2022-03-15 13:29:32 +01:00
2023-04-10 17:32:42 +02:00
/**
* Do not change the data . internal_children * cache counters here .
* Because if nodes are re - added , the indices can remain
* greater - than - everything indices and children added remain
* properly ordered .
*
* All children indices and counters will be updated next time the
* cache is re - generated .
*/
2022-03-15 13:29:32 +01:00
2023-04-10 17:32:42 +02:00
data . blocked + + ;
2022-03-15 13:29:32 +01:00
p_child - > _set_tree ( nullptr ) ;
remove_child_notify ( p_child ) ;
p_child - > notification ( NOTIFICATION_UNPARENTED ) ;
2023-04-10 17:32:42 +02:00
data . blocked - - ;
2022-03-15 13:29:32 +01:00
2023-04-10 17:32:42 +02:00
data . children_cache_dirty = true ;
bool success = data . children . erase ( p_child - > data . name ) ;
ERR_FAIL_COND_MSG ( ! success , " Children name does not match parent name in hashtable, this is a bug. " ) ;
2022-03-15 13:29:32 +01:00
2023-04-10 15:55:34 +02:00
notification ( NOTIFICATION_CHILD_ORDER_CHANGED ) ;
emit_signal ( SceneStringNames : : get_singleton ( ) - > child_order_changed ) ;
2022-03-15 13:29:32 +01:00
p_child - > data . parent = nullptr ;
p_child - > data . pos = - 1 ;
p_child - > _propagate_after_exit_branch ( data . inside_tree ) ;
}
2023-04-10 17:32:42 +02:00
void Node : : _update_children_cache_impl ( ) const {
// Assign children
data . children_cache . resize ( data . children . size ( ) ) ;
int idx = 0 ;
for ( const HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
data . children_cache [ idx ] = E - > value ( ) ;
idx + + ;
}
// Sort them
data . children_cache . sort_custom < ComparatorByIndex > ( ) ;
// Update indices
for ( uint32_t i = 0 ; i < data . children_cache . size ( ) ; i + + ) {
data . children_cache [ i ] - > data . pos = i ;
}
data . children_cache_dirty = false ;
}
2022-03-15 13:29:32 +01:00
int Node : : get_child_count ( ) const {
return data . children . size ( ) ;
}
Node * Node : : get_child ( int p_index ) const {
2023-04-10 17:32:42 +02:00
ERR_FAIL_INDEX_V ( static_cast < uint32_t > ( p_index ) , data . children . size ( ) , nullptr ) ;
2022-03-15 13:29:32 +01:00
2023-04-10 17:32:42 +02:00
_update_children_cache ( ) ;
return data . children_cache [ p_index ] ;
2022-03-15 13:29:32 +01:00
}
Node * Node : : _get_child_by_name ( const StringName & p_name ) const {
2023-04-10 17:32:42 +02:00
const Node * const * node = data . children . getptr ( p_name ) ;
if ( node ) {
return const_cast < Node * > ( * node ) ;
} else {
return nullptr ;
2022-03-15 13:29:32 +01:00
}
}
Node * Node : : get_node_or_null ( const NodePath & p_path ) const {
if ( p_path . is_empty ( ) ) {
return nullptr ;
}
ERR_FAIL_COND_V_MSG ( ! data . inside_tree & & p_path . is_absolute ( ) , nullptr , " Can't use get_node() with absolute paths from outside the active scene tree. " ) ;
Node * current = nullptr ;
Node * root = nullptr ;
if ( ! p_path . is_absolute ( ) ) {
current = const_cast < Node * > ( this ) ; //start from this
} else {
root = const_cast < Node * > ( this ) ;
while ( root - > data . parent ) {
root = root - > data . parent ; //start from root
}
}
for ( int i = 0 ; i < p_path . get_name_count ( ) ; i + + ) {
StringName name = p_path . get_name ( i ) ;
Node * next = nullptr ;
if ( name = = SceneStringNames : : get_singleton ( ) - > dot ) { // .
next = current ;
} else if ( name = = SceneStringNames : : get_singleton ( ) - > doubledot ) { // ..
if ( current = = nullptr | | ! current - > data . parent ) {
return nullptr ;
}
next = current - > data . parent ;
} else if ( current = = nullptr ) {
if ( name = = root - > get_name ( ) ) {
next = root ;
}
2022-07-27 00:45:48 +02:00
} else if ( name . is_node_unique_name ( ) ) {
if ( current - > data . owned_unique_nodes . size ( ) ) {
// Has unique nodes in ownership
Node * * unique = current - > data . owned_unique_nodes . getptr ( name ) ;
if ( ! unique ) {
return nullptr ;
}
next = * unique ;
} else if ( current - > data . owner ) {
Node * * unique = current - > data . owner - > data . owned_unique_nodes . getptr ( name ) ;
if ( ! unique ) {
return nullptr ;
}
next = * unique ;
} else {
return nullptr ;
}
2022-03-15 13:29:32 +01:00
} else {
next = nullptr ;
2023-04-10 17:32:42 +02:00
const Node * const * node = current - > data . children . getptr ( name ) ;
if ( node ) {
next = const_cast < Node * > ( * node ) ;
} else {
2022-03-15 13:29:32 +01:00
return nullptr ;
2023-04-10 17:32:42 +02:00
}
2022-03-15 13:29:32 +01:00
}
current = next ;
}
return current ;
}
Node * Node : : get_node ( const NodePath & p_path ) const {
Node * node = get_node_or_null ( p_path ) ;
if ( unlikely ( ! node ) ) {
2022-10-19 07:05:41 +02:00
// Try to get a clear description of this node in the error message.
String desc ;
if ( is_inside_tree ( ) ) {
desc = get_path ( ) ;
} else {
desc = get_name ( ) ;
if ( desc . empty ( ) ) {
desc = get_class ( ) ;
}
}
2022-03-15 13:29:32 +01:00
if ( p_path . is_absolute ( ) ) {
ERR_FAIL_V_MSG ( nullptr ,
2022-10-19 07:05:41 +02:00
vformat ( " (Node not found: \" %s \" (absolute path attempted from \" %s \" ).) " , p_path , desc ) ) ;
2022-03-15 13:29:32 +01:00
} else {
ERR_FAIL_V_MSG ( nullptr ,
2022-10-19 07:05:41 +02:00
vformat ( " (Node not found: \" %s \" (relative to \" %s \" ).) " , p_path , desc ) ) ;
2022-03-15 13:29:32 +01:00
}
}
return node ;
}
bool Node : : has_node ( const NodePath & p_path ) const {
return get_node_or_null ( p_path ) ! = nullptr ;
}
Node * Node : : find_node ( const String & p_mask , bool p_recursive , bool p_owned ) const {
2023-04-10 17:32:42 +02:00
ERR_FAIL_COND_V ( p_mask . empty ( ) , NULL ) ;
_update_children_cache ( ) ;
Node * const * cptr = data . children_cache . ptr ( ) ;
int ccount = data . children_cache . size ( ) ;
2022-03-15 13:29:32 +01:00
for ( int i = 0 ; i < ccount ; i + + ) {
if ( p_owned & & ! cptr [ i ] - > data . owner ) {
continue ;
}
if ( cptr [ i ] - > data . name . operator String ( ) . match ( p_mask ) ) {
return cptr [ i ] ;
}
if ( ! p_recursive ) {
continue ;
}
Node * ret = cptr [ i ] - > find_node ( p_mask , true , p_owned ) ;
if ( ret ) {
return ret ;
}
}
return nullptr ;
}
Node * Node : : get_parent ( ) const {
return data . parent ;
}
Node * Node : : find_parent ( const String & p_mask ) const {
Node * p = data . parent ;
while ( p ) {
if ( p - > data . name . operator String ( ) . match ( p_mask ) ) {
return p ;
}
p = p - > data . parent ;
}
return nullptr ;
}
bool Node : : is_a_parent_of ( const Node * p_node ) const {
ERR_FAIL_NULL_V ( p_node , false ) ;
Node * p = p_node - > data . parent ;
while ( p ) {
if ( p = = this ) {
return true ;
}
p = p - > data . parent ;
}
return false ;
}
bool Node : : is_greater_than ( const Node * p_node ) const {
ERR_FAIL_NULL_V ( p_node , false ) ;
ERR_FAIL_COND_V ( ! data . inside_tree , false ) ;
ERR_FAIL_COND_V ( ! p_node - > data . inside_tree , false ) ;
ERR_FAIL_COND_V ( data . depth < 0 , false ) ;
ERR_FAIL_COND_V ( p_node - > data . depth < 0 , false ) ;
2023-04-10 17:32:42 +02:00
_update_children_cache ( ) ;
2022-03-15 13:29:32 +01:00
# ifdef NO_ALLOCA
Vector < int > this_stack ;
Vector < int > that_stack ;
this_stack . resize ( data . depth ) ;
that_stack . resize ( p_node - > data . depth ) ;
# else
int * this_stack = ( int * ) alloca ( sizeof ( int ) * data . depth ) ;
int * that_stack = ( int * ) alloca ( sizeof ( int ) * p_node - > data . depth ) ;
# endif
const Node * n = this ;
int idx = data . depth - 1 ;
while ( n ) {
ERR_FAIL_INDEX_V ( idx , data . depth , false ) ;
2023-04-10 17:32:42 +02:00
this_stack [ idx - - ] = n - > get_index ( ) ;
2022-03-15 13:29:32 +01:00
n = n - > data . parent ;
}
ERR_FAIL_COND_V ( idx ! = - 1 , false ) ;
n = p_node ;
idx = p_node - > data . depth - 1 ;
while ( n ) {
ERR_FAIL_INDEX_V ( idx , p_node - > data . depth , false ) ;
2023-04-10 17:32:42 +02:00
that_stack [ idx - - ] = n - > get_index ( ) ;
2022-03-15 13:29:32 +01:00
n = n - > data . parent ;
}
ERR_FAIL_COND_V ( idx ! = - 1 , false ) ;
idx = 0 ;
bool res ;
while ( true ) {
// using -2 since out-of-tree or nonroot nodes have -1
int this_idx = ( idx > = data . depth ) ? - 2 : this_stack [ idx ] ;
int that_idx = ( idx > = p_node - > data . depth ) ? - 2 : that_stack [ idx ] ;
if ( this_idx > that_idx ) {
res = true ;
break ;
} else if ( this_idx < that_idx ) {
res = false ;
break ;
} else if ( this_idx = = - 2 ) {
res = false ; // equal
break ;
}
idx + + ;
}
return res ;
}
void Node : : get_owned_by ( Node * p_by , List < Node * > * p_owned ) {
if ( data . owner = = p_by ) {
p_owned - > push_back ( this ) ;
}
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > get_owned_by ( p_by , p_owned ) ;
2022-03-15 13:29:32 +01:00
}
}
void Node : : _set_owner_nocheck ( Node * p_owner ) {
if ( data . owner = = p_owner ) {
return ;
}
ERR_FAIL_COND ( data . owner ) ;
data . owner = p_owner ;
data . owner - > data . owned . push_back ( this ) ;
data . OW = data . owner - > data . owned . back ( ) ;
2022-07-29 19:28:33 +02:00
owner_changed_notify ( ) ;
2022-03-15 13:29:32 +01:00
}
2022-07-27 00:45:48 +02:00
void Node : : _release_unique_name_in_owner ( ) {
ERR_FAIL_NULL ( data . owner ) ; // Sanity check.
StringName key = StringName ( " % " + data . name . operator String ( ) ) ;
Node * * which = data . owner - > data . owned_unique_nodes . getptr ( key ) ;
if ( which = = nullptr | | * which ! = this ) {
return ; // Ignore.
}
data . owner - > data . owned_unique_nodes . erase ( key ) ;
}
void Node : : _acquire_unique_name_in_owner ( ) {
ERR_FAIL_NULL ( data . owner ) ; // Sanity check.
StringName key = StringName ( " % " + data . name . operator String ( ) ) ;
Node * * which = data . owner - > data . owned_unique_nodes . getptr ( key ) ;
if ( which ! = nullptr & & * which ! = this ) {
WARN_PRINT ( vformat ( RTR ( " Setting node name '%s' to be unique within scene for '%s', but it's already claimed by '%s'. This node is no longer set unique. " ) , get_name ( ) , is_inside_tree ( ) ? get_path ( ) : data . owner - > get_path_to ( this ) , is_inside_tree ( ) ? ( * which ) - > get_path ( ) : data . owner - > get_path_to ( * which ) ) ) ;
data . unique_name_in_owner = false ;
return ;
}
data . owner - > data . owned_unique_nodes [ key ] = this ;
}
void Node : : set_unique_name_in_owner ( bool p_enabled ) {
if ( data . unique_name_in_owner = = p_enabled ) {
return ;
}
if ( data . unique_name_in_owner & & data . owner ! = nullptr ) {
_release_unique_name_in_owner ( ) ;
}
data . unique_name_in_owner = p_enabled ;
if ( data . unique_name_in_owner & & data . owner ! = nullptr ) {
_acquire_unique_name_in_owner ( ) ;
}
update_configuration_warning ( ) ;
}
bool Node : : is_unique_name_in_owner ( ) const {
return data . unique_name_in_owner ;
}
2022-03-15 13:29:32 +01:00
void Node : : set_owner ( Node * p_owner ) {
if ( data . owner ) {
2022-07-27 00:45:48 +02:00
if ( data . unique_name_in_owner ) {
_release_unique_name_in_owner ( ) ;
}
2022-03-15 13:29:32 +01:00
data . owner - > data . owned . erase ( data . OW ) ;
data . OW = nullptr ;
data . owner = nullptr ;
}
ERR_FAIL_COND ( p_owner = = this ) ;
if ( ! p_owner ) {
return ;
}
Node * check = this - > get_parent ( ) ;
bool owner_valid = false ;
while ( check ) {
if ( check = = p_owner ) {
owner_valid = true ;
break ;
}
check = check - > data . parent ;
}
ERR_FAIL_COND ( ! owner_valid ) ;
_set_owner_nocheck ( p_owner ) ;
2022-07-27 00:45:48 +02:00
if ( data . unique_name_in_owner ) {
_acquire_unique_name_in_owner ( ) ;
}
2022-03-15 13:29:32 +01:00
}
Node * Node : : get_owner ( ) const {
return data . owner ;
}
Node * Node : : find_common_parent_with ( const Node * p_node ) const {
if ( this = = p_node ) {
return const_cast < Node * > ( p_node ) ;
}
2023-01-15 19:42:08 +01:00
RBSet < const Node * > visited ;
2022-03-15 13:29:32 +01:00
const Node * n = this ;
while ( n ) {
visited . insert ( n ) ;
n = n - > data . parent ;
}
const Node * common_parent = p_node ;
while ( common_parent ) {
if ( visited . has ( common_parent ) ) {
break ;
}
common_parent = common_parent - > data . parent ;
}
if ( ! common_parent ) {
return nullptr ;
}
return const_cast < Node * > ( common_parent ) ;
}
NodePath Node : : get_path_to ( const Node * p_node ) const {
ERR_FAIL_NULL_V ( p_node , NodePath ( ) ) ;
if ( this = = p_node ) {
return NodePath ( " . " ) ;
}
2023-01-15 19:42:08 +01:00
RBSet < const Node * > visited ;
2022-03-15 13:29:32 +01:00
const Node * n = this ;
while ( n ) {
visited . insert ( n ) ;
n = n - > data . parent ;
}
const Node * common_parent = p_node ;
while ( common_parent ) {
if ( visited . has ( common_parent ) ) {
break ;
}
common_parent = common_parent - > data . parent ;
}
ERR_FAIL_COND_V ( ! common_parent , NodePath ( ) ) ; //nodes not in the same tree
visited . clear ( ) ;
Vector < StringName > path ;
n = p_node ;
while ( n ! = common_parent ) {
path . push_back ( n - > get_name ( ) ) ;
n = n - > data . parent ;
}
n = this ;
StringName up = String ( " .. " ) ;
while ( n ! = common_parent ) {
path . push_back ( up ) ;
n = n - > data . parent ;
}
path . invert ( ) ;
return NodePath ( path , false ) ;
}
NodePath Node : : get_path ( ) const {
ERR_FAIL_COND_V_MSG ( ! is_inside_tree ( ) , NodePath ( ) , " Cannot get path of node as it is not in a scene tree. " ) ;
if ( data . path_cache ) {
return * data . path_cache ;
}
const Node * n = this ;
Vector < StringName > path ;
while ( n ) {
path . push_back ( n - > get_name ( ) ) ;
n = n - > data . parent ;
}
path . invert ( ) ;
data . path_cache = memnew ( NodePath ( path , true ) ) ;
return * data . path_cache ;
}
bool Node : : is_in_group ( const StringName & p_identifier ) const {
return data . grouped . has ( p_identifier ) ;
}
void Node : : add_to_group ( const StringName & p_identifier , bool p_persistent ) {
ERR_FAIL_COND ( ! p_identifier . operator String ( ) . length ( ) ) ;
if ( data . grouped . has ( p_identifier ) ) {
return ;
}
GroupData gd ;
if ( data . tree ) {
gd . group = data . tree - > add_to_group ( p_identifier , this ) ;
} else {
gd . group = nullptr ;
}
gd . persistent = p_persistent ;
data . grouped [ p_identifier ] = gd ;
}
void Node : : remove_from_group ( const StringName & p_identifier ) {
ERR_FAIL_COND ( ! data . grouped . has ( p_identifier ) ) ;
2023-01-15 19:12:50 +01:00
RBMap < StringName , GroupData > : : Element * E = data . grouped . find ( p_identifier ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND ( ! E ) ;
if ( data . tree ) {
data . tree - > remove_from_group ( E - > key ( ) , this ) ;
}
data . grouped . erase ( E ) ;
}
Array Node : : _get_groups ( ) const {
Array groups ;
List < GroupInfo > gi ;
get_groups ( & gi ) ;
for ( List < GroupInfo > : : Element * E = gi . front ( ) ; E ; E = E - > next ( ) ) {
groups . push_back ( E - > get ( ) . name ) ;
}
return groups ;
}
void Node : : get_groups ( List < GroupInfo > * p_groups ) const {
2023-01-15 19:12:50 +01:00
for ( const RBMap < StringName , GroupData > : : Element * E = data . grouped . front ( ) ; E ; E = E - > next ( ) ) {
2022-03-15 13:29:32 +01:00
GroupInfo gi ;
gi . name = E - > key ( ) ;
gi . persistent = E - > get ( ) . persistent ;
p_groups - > push_back ( gi ) ;
}
}
int Node : : get_persistent_group_count ( ) const {
int count = 0 ;
2023-01-15 19:12:50 +01:00
for ( const RBMap < StringName , GroupData > : : Element * E = data . grouped . front ( ) ; E ; E = E - > next ( ) ) {
2022-03-15 13:29:32 +01:00
if ( E - > get ( ) . persistent ) {
count + = 1 ;
}
}
return count ;
}
void Node : : _print_tree_pretty ( const String & prefix , const bool last ) {
String new_prefix = last ? String : : utf8 ( " ┖╴ " ) : String : : utf8 ( " ┠╴ " ) ;
print_line ( prefix + new_prefix + String ( get_name ( ) ) ) ;
2023-04-10 17:32:42 +02:00
_update_children_cache ( ) ;
for ( uint32_t i = 0 ; i < data . children_cache . size ( ) ; i + + ) {
2022-03-15 13:29:32 +01:00
new_prefix = last ? String : : utf8 ( " " ) : String : : utf8 ( " ┃ " ) ;
2023-04-10 17:32:42 +02:00
data . children_cache [ i ] - > _print_tree_pretty ( prefix + new_prefix , i = = data . children_cache . size ( ) - 1 ) ;
2022-03-15 13:29:32 +01:00
}
}
void Node : : print_tree_pretty ( ) {
_print_tree_pretty ( " " , true ) ;
}
void Node : : print_tree ( ) {
_print_tree ( this ) ;
}
void Node : : _print_tree ( const Node * p_node ) {
print_line ( String ( p_node - > get_path_to ( this ) ) ) ;
2023-04-10 17:32:42 +02:00
_update_children_cache ( ) ;
for ( uint32_t i = 0 ; i < data . children_cache . size ( ) ; i + + ) {
data . children_cache [ i ] - > _print_tree ( p_node ) ;
2022-03-15 13:29:32 +01:00
}
}
void Node : : _propagate_reverse_notification ( int p_notification ) {
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . back ( ) ; E ; E = E - > prev ) {
E - > value ( ) - > _propagate_reverse_notification ( p_notification ) ;
2022-03-15 13:29:32 +01:00
}
notification ( p_notification , true ) ;
data . blocked - - ;
}
void Node : : _propagate_deferred_notification ( int p_notification , bool p_reverse ) {
ERR_FAIL_COND ( ! is_inside_tree ( ) ) ;
data . blocked + + ;
if ( ! p_reverse ) {
MessageQueue : : get_singleton ( ) - > push_notification ( this , p_notification ) ;
}
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_deferred_notification ( p_notification , p_reverse ) ;
2022-03-15 13:29:32 +01:00
}
if ( p_reverse ) {
MessageQueue : : get_singleton ( ) - > push_notification ( this , p_notification ) ;
}
data . blocked - - ;
}
void Node : : propagate_notification ( int p_notification ) {
data . blocked + + ;
notification ( p_notification ) ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > propagate_notification ( p_notification ) ;
2022-03-15 13:29:32 +01:00
}
2023-04-10 17:32:42 +02:00
2022-03-15 13:29:32 +01:00
data . blocked - - ;
}
void Node : : propagate_call ( const StringName & p_method , const Array & p_args , const bool p_parent_first ) {
data . blocked + + ;
if ( p_parent_first & & has_method ( p_method ) ) {
callv ( p_method , p_args ) ;
}
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > propagate_call ( p_method , p_args , p_parent_first ) ;
2022-03-15 13:29:32 +01:00
}
if ( ! p_parent_first & & has_method ( p_method ) ) {
callv ( p_method , p_args ) ;
}
data . blocked - - ;
}
void Node : : _propagate_replace_owner ( Node * p_owner , Node * p_by_owner ) {
if ( get_owner ( ) = = p_owner ) {
set_owner ( p_by_owner ) ;
}
data . blocked + + ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > _propagate_replace_owner ( p_owner , p_by_owner ) ;
2022-03-15 13:29:32 +01:00
}
data . blocked - - ;
}
2022-07-27 13:37:17 +02:00
Ref < SceneTreeTween > Node : : create_tween ( ) {
ERR_FAIL_COND_V_MSG ( ! data . tree , nullptr , " Can't create SceneTreeTween when not inside scene tree. " ) ;
Ref < SceneTreeTween > tween = get_tree ( ) - > create_tween ( ) ;
tween - > bind_node ( this ) ;
return tween ;
}
2022-03-15 13:29:32 +01:00
void Node : : remove_and_skip ( ) {
ERR_FAIL_COND ( ! data . parent ) ;
2023-04-10 17:32:42 +02:00
_update_children_cache ( ) ;
2022-03-15 13:29:32 +01:00
Node * new_owner = get_owner ( ) ;
List < Node * > children ;
while ( true ) {
bool clear = true ;
2023-04-10 17:32:42 +02:00
for ( uint32_t i = 0 ; i < data . children_cache . size ( ) ; i + + ) {
Node * c_node = data . children_cache [ i ] ;
2022-03-15 13:29:32 +01:00
if ( ! c_node - > get_owner ( ) ) {
continue ;
}
remove_child ( c_node ) ;
c_node - > _propagate_replace_owner ( this , nullptr ) ;
children . push_back ( c_node ) ;
clear = false ;
break ;
}
if ( clear ) {
break ;
}
}
while ( ! children . empty ( ) ) {
Node * c_node = children . front ( ) - > get ( ) ;
data . parent - > add_child ( c_node ) ;
c_node - > _propagate_replace_owner ( nullptr , new_owner ) ;
children . pop_front ( ) ;
}
data . parent - > remove_child ( this ) ;
}
void Node : : set_filename ( const String & p_filename ) {
data . filename = p_filename ;
}
String Node : : get_filename ( ) const {
return data . filename ;
}
void Node : : set_editor_description ( const String & p_editor_description ) {
set_meta ( " _editor_description_ " , p_editor_description ) ;
}
String Node : : get_editor_description ( ) const {
if ( has_meta ( " _editor_description_ " ) ) {
return get_meta ( " _editor_description_ " ) ;
} else {
return " " ;
}
}
void Node : : set_editable_instance ( Node * p_node , bool p_editable ) {
ERR_FAIL_NULL ( p_node ) ;
ERR_FAIL_COND ( ! is_a_parent_of ( p_node ) ) ;
if ( ! p_editable ) {
p_node - > data . editable_instance = false ;
// Avoid this flag being needlessly saved;
// also give more visual feedback if editable children is re-enabled
set_display_folded ( false ) ;
} else {
p_node - > data . editable_instance = true ;
}
}
bool Node : : is_editable_instance ( const Node * p_node ) const {
if ( ! p_node ) {
return false ; //easier, null is never editable :)
}
ERR_FAIL_COND_V ( ! is_a_parent_of ( p_node ) , false ) ;
return p_node - > data . editable_instance ;
}
Node * Node : : get_deepest_editable_node ( Node * p_start_node ) const {
ERR_FAIL_NULL_V ( p_start_node , nullptr ) ;
ERR_FAIL_COND_V ( ! is_a_parent_of ( p_start_node ) , p_start_node ) ;
Node const * iterated_item = p_start_node ;
Node * node = p_start_node ;
while ( iterated_item - > get_owner ( ) & & iterated_item - > get_owner ( ) ! = this ) {
if ( ! is_editable_instance ( iterated_item - > get_owner ( ) ) ) {
node = iterated_item - > get_owner ( ) ;
}
iterated_item = iterated_item - > get_owner ( ) ;
}
return node ;
}
# ifdef TOOLS_ENABLED
void Node : : set_property_pinned ( const StringName & p_property , bool p_pinned ) {
bool current_pinned = false ;
bool has_pinned = has_meta ( " _edit_pinned_properties_ " ) ;
Array pinned ;
String psa = get_property_store_alias ( p_property ) ;
if ( has_pinned ) {
pinned = get_meta ( " _edit_pinned_properties_ " ) ;
current_pinned = pinned . has ( psa ) ;
}
if ( current_pinned ! = p_pinned ) {
if ( p_pinned ) {
pinned . append ( psa ) ;
if ( ! has_pinned ) {
set_meta ( " _edit_pinned_properties_ " , pinned ) ;
}
} else {
pinned . erase ( psa ) ;
if ( pinned . empty ( ) ) {
remove_meta ( " _edit_pinned_properties_ " ) ;
}
}
}
}
bool Node : : is_property_pinned ( const StringName & p_property ) const {
if ( ! has_meta ( " _edit_pinned_properties_ " ) ) {
return false ;
}
Array pinned = get_meta ( " _edit_pinned_properties_ " ) ;
String psa = get_property_store_alias ( p_property ) ;
return pinned . has ( psa ) ;
}
StringName Node : : get_property_store_alias ( const StringName & p_property ) const {
return p_property ;
}
# endif
2023-01-15 19:42:08 +01:00
void Node : : get_storable_properties ( RBSet < StringName > & r_storable_properties ) const {
2022-03-15 13:29:32 +01:00
List < PropertyInfo > pi ;
get_property_list ( & pi ) ;
for ( List < PropertyInfo > : : Element * E = pi . front ( ) ; E ; E = E - > next ( ) ) {
if ( ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
r_storable_properties . insert ( E - > get ( ) . name ) ;
}
}
}
String Node : : to_string ( ) {
2022-09-20 09:10:08 +02:00
// This code doesn't print the script's name, it calls to_string() if you override it in a Node's script,
// which you only do if you specifically want to customize how the node should be represented by print().
2022-03-15 13:29:32 +01:00
if ( get_script_instance ( ) ) {
bool valid ;
String ret = get_script_instance ( ) - > to_string ( & valid ) ;
if ( valid ) {
return ret ;
}
}
return ( get_name ( ) ? String ( get_name ( ) ) + " : " : " " ) + Object : : to_string ( ) ;
}
void Node : : set_scene_instance_state ( const Ref < SceneState > & p_state ) {
data . instance_state = p_state ;
}
Ref < SceneState > Node : : get_scene_instance_state ( ) const {
return data . instance_state ;
}
void Node : : set_scene_inherited_state ( const Ref < SceneState > & p_state ) {
data . inherited_state = p_state ;
}
Ref < SceneState > Node : : get_scene_inherited_state ( ) const {
return data . inherited_state ;
}
void Node : : set_scene_instance_load_placeholder ( bool p_enable ) {
data . use_placeholder = p_enable ;
}
bool Node : : get_scene_instance_load_placeholder ( ) const {
return data . use_placeholder ;
}
int Node : : get_position_in_parent ( ) const {
return data . pos ;
}
2023-01-15 19:12:50 +01:00
Node * Node : : _duplicate ( int p_flags , RBMap < const Node * , Node * > * r_duplimap ) const {
2022-03-15 13:29:32 +01:00
Node * node = nullptr ;
bool instanced = false ;
if ( Object : : cast_to < InstancePlaceholder > ( this ) ) {
const InstancePlaceholder * ip = Object : : cast_to < const InstancePlaceholder > ( this ) ;
InstancePlaceholder * nip = memnew ( InstancePlaceholder ) ;
nip - > set_instance_path ( ip - > get_instance_path ( ) ) ;
node = nip ;
} else if ( ( p_flags & DUPLICATE_USE_INSTANCING ) & & get_filename ( ) ! = String ( ) ) {
Ref < PackedScene > res = ResourceLoader : : load ( get_filename ( ) ) ;
ERR_FAIL_COND_V ( res . is_null ( ) , nullptr ) ;
PackedScene : : GenEditState ges = PackedScene : : GEN_EDIT_STATE_DISABLED ;
# ifdef TOOLS_ENABLED
if ( p_flags & DUPLICATE_FROM_EDITOR ) {
ges = PackedScene : : GEN_EDIT_STATE_INSTANCE ;
}
# endif
node = res - > instance ( ges ) ;
ERR_FAIL_COND_V ( ! node , nullptr ) ;
node - > set_scene_instance_load_placeholder ( get_scene_instance_load_placeholder ( ) ) ;
instanced = true ;
} else {
Object * obj = ClassDB : : instance ( get_class ( ) ) ;
ERR_FAIL_COND_V ( ! obj , nullptr ) ;
node = Object : : cast_to < Node > ( obj ) ;
if ( ! node ) {
memdelete ( obj ) ;
}
ERR_FAIL_COND_V ( ! node , nullptr ) ;
}
if ( get_filename ( ) ! = " " ) { //an instance
node - > set_filename ( get_filename ( ) ) ;
node - > data . editable_instance = data . editable_instance ;
}
StringName script_property_name = CoreStringNames : : get_singleton ( ) - > _script ;
List < const Node * > hidden_roots ;
List < const Node * > node_tree ;
node_tree . push_front ( this ) ;
if ( instanced ) {
// Since nodes in the instanced hierarchy won't be duplicated explicitly, we need to make an inventory
// of all the nodes in the tree of the instanced scene in order to transfer the values of the properties
Vector < const Node * > instance_roots ;
instance_roots . push_back ( this ) ;
for ( List < const Node * > : : Element * N = node_tree . front ( ) ; N ; N = N - > next ( ) ) {
for ( int i = 0 ; i < N - > get ( ) - > get_child_count ( ) ; + + i ) {
Node * descendant = N - > get ( ) - > get_child ( i ) ;
// Skip nodes not really belonging to the instanced hierarchy; they'll be processed normally later
// but remember non-instanced nodes that are hidden below instanced ones
if ( instance_roots . find ( descendant - > get_owner ( ) ) = = - 1 ) {
if ( descendant - > get_parent ( ) & & descendant - > get_parent ( ) ! = this & & descendant - > data . owner ! = descendant - > get_parent ( ) ) {
hidden_roots . push_back ( descendant ) ;
}
continue ;
}
node_tree . push_back ( descendant ) ;
if ( descendant - > get_filename ( ) ! = " " & & instance_roots . find ( descendant - > get_owner ( ) ) ! = - 1 ) {
instance_roots . push_back ( descendant ) ;
}
}
}
}
for ( List < const Node * > : : Element * N = node_tree . front ( ) ; N ; N = N - > next ( ) ) {
Node * current_node = node - > get_node ( get_path_to ( N - > get ( ) ) ) ;
ERR_CONTINUE ( ! current_node ) ;
if ( p_flags & DUPLICATE_SCRIPTS ) {
bool is_valid = false ;
Variant script = N - > get ( ) - > get ( script_property_name , & is_valid ) ;
if ( is_valid ) {
current_node - > set ( script_property_name , script ) ;
}
}
List < PropertyInfo > plist ;
N - > get ( ) - > get_property_list ( & plist ) ;
for ( List < PropertyInfo > : : Element * E = plist . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
continue ;
}
String name = E - > get ( ) . name ;
if ( name = = script_property_name ) {
continue ;
}
Variant value = N - > get ( ) - > get ( name ) . duplicate ( true ) ;
if ( E - > get ( ) . usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE ) {
Resource * res = Object : : cast_to < Resource > ( value ) ;
if ( res ) { // Duplicate only if it's a resource
current_node - > set ( name , res - > duplicate ( ) ) ;
}
} else {
current_node - > set ( name , value ) ;
}
}
}
if ( get_name ( ) ! = String ( ) ) {
node - > set_name ( get_name ( ) ) ;
}
# ifdef TOOLS_ENABLED
if ( ( p_flags & DUPLICATE_FROM_EDITOR ) & & r_duplimap ) {
r_duplimap - > insert ( this , node ) ;
}
# endif
if ( p_flags & DUPLICATE_GROUPS ) {
List < GroupInfo > gi ;
get_groups ( & gi ) ;
for ( List < GroupInfo > : : Element * E = gi . front ( ) ; E ; E = E - > next ( ) ) {
# ifdef TOOLS_ENABLED
if ( ( p_flags & DUPLICATE_FROM_EDITOR ) & & ! E - > get ( ) . persistent ) {
continue ;
}
# endif
node - > add_to_group ( E - > get ( ) . name , E - > get ( ) . persistent ) ;
}
}
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
if ( get_child ( i ) - > data . parent_owned ) {
continue ;
}
if ( instanced & & get_child ( i ) - > data . owner = = this ) {
continue ; //part of instance
}
Node * dup = get_child ( i ) - > _duplicate ( p_flags , r_duplimap ) ;
if ( ! dup ) {
memdelete ( node ) ;
return nullptr ;
}
node - > add_child ( dup ) ;
if ( i < node - > get_child_count ( ) - 1 ) {
node - > move_child ( dup , i ) ;
}
}
for ( List < const Node * > : : Element * E = hidden_roots . front ( ) ; E ; E = E - > next ( ) ) {
Node * parent = node - > get_node ( get_path_to ( E - > get ( ) - > data . parent ) ) ;
if ( ! parent ) {
memdelete ( node ) ;
return nullptr ;
}
Node * dup = E - > get ( ) - > _duplicate ( p_flags , r_duplimap ) ;
if ( ! dup ) {
memdelete ( node ) ;
return nullptr ;
}
parent - > add_child ( dup ) ;
int pos = E - > get ( ) - > get_position_in_parent ( ) ;
if ( pos < parent - > get_child_count ( ) - 1 ) {
parent - > move_child ( dup , pos ) ;
}
}
return node ;
}
Node * Node : : duplicate ( int p_flags ) const {
Node * dupe = _duplicate ( p_flags ) ;
if ( dupe & & ( p_flags & DUPLICATE_SIGNALS ) ) {
_duplicate_signals ( this , dupe ) ;
}
return dupe ;
}
# ifdef TOOLS_ENABLED
2023-01-15 19:12:50 +01:00
Node * Node : : duplicate_from_editor ( RBMap < const Node * , Node * > & r_duplimap ) const {
return duplicate_from_editor ( r_duplimap , RBMap < RES , RES > ( ) ) ;
2022-03-15 13:29:32 +01:00
}
2023-01-15 19:12:50 +01:00
Node * Node : : duplicate_from_editor ( RBMap < const Node * , Node * > & r_duplimap , const RBMap < RES , RES > & p_resource_remap ) const {
2022-03-15 13:29:32 +01:00
Node * dupe = _duplicate ( DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR , & r_duplimap ) ;
// This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated.
if ( ! p_resource_remap . empty ( ) ) {
remap_node_resources ( dupe , p_resource_remap ) ;
}
// Duplication of signals must happen after all the node descendants have been copied,
// because re-targeting of connections from some descendant to another is not possible
// if the emitter node comes later in tree order than the receiver
_duplicate_signals ( this , dupe ) ;
return dupe ;
}
2023-01-15 19:12:50 +01:00
void Node : : remap_node_resources ( Node * p_node , const RBMap < RES , RES > & p_resource_remap ) const {
2022-03-15 13:29:32 +01:00
List < PropertyInfo > props ;
p_node - > get_property_list ( & props ) ;
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
continue ;
}
Variant v = p_node - > get ( E - > get ( ) . name ) ;
if ( v . is_ref ( ) ) {
RES res = v ;
if ( res . is_valid ( ) ) {
if ( p_resource_remap . has ( res ) ) {
p_node - > set ( E - > get ( ) . name , p_resource_remap [ res ] ) ;
remap_nested_resources ( res , p_resource_remap ) ;
}
}
}
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
remap_node_resources ( p_node - > get_child ( i ) , p_resource_remap ) ;
}
}
2023-01-15 19:12:50 +01:00
void Node : : remap_nested_resources ( RES p_resource , const RBMap < RES , RES > & p_resource_remap ) const {
2022-03-15 13:29:32 +01:00
List < PropertyInfo > props ;
p_resource - > get_property_list ( & props ) ;
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
continue ;
}
Variant v = p_resource - > get ( E - > get ( ) . name ) ;
if ( v . is_ref ( ) ) {
RES res = v ;
if ( res . is_valid ( ) ) {
if ( p_resource_remap . has ( res ) ) {
p_resource - > set ( E - > get ( ) . name , p_resource_remap [ res ] ) ;
remap_nested_resources ( res , p_resource_remap ) ;
}
}
}
}
}
# endif
2023-01-15 19:12:50 +01:00
void Node : : _duplicate_and_reown ( Node * p_new_parent , const RBMap < Node * , Node * > & p_reown_map ) const {
2022-03-15 13:29:32 +01:00
if ( get_owner ( ) ! = get_parent ( ) - > get_owner ( ) ) {
return ;
}
Node * node = nullptr ;
if ( get_filename ( ) ! = " " ) {
Ref < PackedScene > res = ResourceLoader : : load ( get_filename ( ) ) ;
ERR_FAIL_COND_MSG ( res . is_null ( ) , " Cannot load scene: " + get_filename ( ) ) ;
node = res - > instance ( ) ;
ERR_FAIL_COND ( ! node ) ;
} else {
Object * obj = ClassDB : : instance ( get_class ( ) ) ;
ERR_FAIL_COND_MSG ( ! obj , " Node: Could not duplicate: " + String ( get_class ( ) ) + " . " ) ;
node = Object : : cast_to < Node > ( obj ) ;
if ( ! node ) {
memdelete ( obj ) ;
ERR_FAIL_MSG ( " Node: Could not duplicate: " + String ( get_class ( ) ) + " . " ) ;
}
}
List < PropertyInfo > plist ;
get_property_list ( & plist ) ;
for ( List < PropertyInfo > : : Element * E = plist . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
continue ;
}
String name = E - > get ( ) . name ;
Variant value = get ( name ) . duplicate ( true ) ;
node - > set ( name , value ) ;
}
List < GroupInfo > groups ;
get_groups ( & groups ) ;
for ( List < GroupInfo > : : Element * E = groups . front ( ) ; E ; E = E - > next ( ) ) {
node - > add_to_group ( E - > get ( ) . name , E - > get ( ) . persistent ) ;
}
node - > set_name ( get_name ( ) ) ;
p_new_parent - > add_child ( node ) ;
Node * owner = get_owner ( ) ;
if ( p_reown_map . has ( owner ) ) {
owner = p_reown_map [ owner ] ;
}
if ( owner ) {
NodePath p = get_path_to ( owner ) ;
if ( owner ! = this ) {
Node * new_owner = node - > get_node ( p ) ;
if ( new_owner ) {
node - > set_owner ( new_owner ) ;
}
}
}
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
get_child ( i ) - > _duplicate_and_reown ( node , p_reown_map ) ;
}
}
// Duplication of signals must happen after all the node descendants have been copied,
// because re-targeting of connections from some descendant to another is not possible
// if the emitter node comes later in tree order than the receiver
void Node : : _duplicate_signals ( const Node * p_original , Node * p_copy ) const {
if ( ( this ! = p_original ) & & ! ( p_original - > is_a_parent_of ( this ) ) ) {
return ;
}
List < const Node * > process_list ;
process_list . push_back ( this ) ;
while ( ! process_list . empty ( ) ) {
const Node * n = process_list . front ( ) - > get ( ) ;
process_list . pop_front ( ) ;
List < Connection > conns ;
n - > get_all_signal_connections ( & conns ) ;
for ( List < Connection > : : Element * E = conns . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . flags & CONNECT_PERSIST ) {
//user connected
NodePath p = p_original - > get_path_to ( n ) ;
Node * copy = p_copy - > get_node ( p ) ;
Node * target = Object : : cast_to < Node > ( E - > get ( ) . target ) ;
if ( ! target ) {
continue ;
}
NodePath ptarget = p_original - > get_path_to ( target ) ;
Node * copytarget = target ;
// Attempt to find a path to the duplicate target, if it seems it's not part
// of the duplicated and not yet parented hierarchy then at least try to connect
// to the same target as the original
if ( p_copy - > has_node ( ptarget ) ) {
copytarget = p_copy - > get_node ( ptarget ) ;
}
if ( copy & & copytarget & & ! copy - > is_connected ( E - > get ( ) . signal , copytarget , E - > get ( ) . method ) ) {
copy - > connect ( E - > get ( ) . signal , copytarget , E - > get ( ) . method , E - > get ( ) . binds , E - > get ( ) . flags ) ;
}
}
}
for ( int i = 0 ; i < n - > get_child_count ( ) ; i + + ) {
process_list . push_back ( n - > get_child ( i ) ) ;
}
}
}
2023-01-15 19:12:50 +01:00
Node * Node : : duplicate_and_reown ( const RBMap < Node * , Node * > & p_reown_map ) const {
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND_V ( get_filename ( ) ! = " " , nullptr ) ;
Object * obj = ClassDB : : instance ( get_class ( ) ) ;
ERR_FAIL_COND_V_MSG ( ! obj , nullptr , " Node: Could not duplicate: " + String ( get_class ( ) ) + " . " ) ;
Node * node = Object : : cast_to < Node > ( obj ) ;
if ( ! node ) {
memdelete ( obj ) ;
ERR_FAIL_V_MSG ( nullptr , " Node: Could not duplicate: " + String ( get_class ( ) ) + " . " ) ;
}
node - > set_name ( get_name ( ) ) ;
List < PropertyInfo > plist ;
get_property_list ( & plist ) ;
for ( List < PropertyInfo > : : Element * E = plist . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
continue ;
}
String name = E - > get ( ) . name ;
node - > set ( name , get ( name ) ) ;
}
List < GroupInfo > groups ;
get_groups ( & groups ) ;
for ( List < GroupInfo > : : Element * E = groups . front ( ) ; E ; E = E - > next ( ) ) {
node - > add_to_group ( E - > get ( ) . name , E - > get ( ) . persistent ) ;
}
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
get_child ( i ) - > _duplicate_and_reown ( node , p_reown_map ) ;
}
// Duplication of signals must happen after all the node descendants have been copied,
// because re-targeting of connections from some descendant to another is not possible
// if the emitter node comes later in tree order than the receiver
_duplicate_signals ( this , node ) ;
return node ;
}
static void find_owned_by ( Node * p_by , Node * p_node , List < Node * > * p_owned ) {
if ( p_node - > get_owner ( ) = = p_by ) {
p_owned - > push_back ( p_node ) ;
}
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
find_owned_by ( p_by , p_node - > get_child ( i ) , p_owned ) ;
}
}
struct _NodeReplaceByPair {
String name ;
Variant value ;
} ;
void Node : : replace_by ( Node * p_node , bool p_keep_data ) {
ERR_FAIL_NULL ( p_node ) ;
ERR_FAIL_COND ( p_node - > data . parent ) ;
List < Node * > owned = data . owned ;
List < Node * > owned_by_owner ;
Node * owner = ( data . owner = = this ) ? p_node : data . owner ;
List < _NodeReplaceByPair > replace_data ;
if ( p_keep_data ) {
List < PropertyInfo > plist ;
get_property_list ( & plist ) ;
for ( List < PropertyInfo > : : Element * E = plist . front ( ) ; E ; E = E - > next ( ) ) {
_NodeReplaceByPair rd ;
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_STORAGE ) ) {
continue ;
}
rd . name = E - > get ( ) . name ;
rd . value = get ( rd . name ) ;
}
List < GroupInfo > groups ;
get_groups ( & groups ) ;
for ( List < GroupInfo > : : Element * E = groups . front ( ) ; E ; E = E - > next ( ) ) {
p_node - > add_to_group ( E - > get ( ) . name , E - > get ( ) . persistent ) ;
}
}
_replace_connections_target ( p_node ) ;
if ( data . owner ) {
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
find_owned_by ( data . owner , get_child ( i ) , & owned_by_owner ) ;
}
}
Node * parent = data . parent ;
int pos_in_parent = data . pos ;
if ( data . parent ) {
parent - > remove_child ( this ) ;
parent - > add_child ( p_node ) ;
parent - > move_child ( p_node , pos_in_parent ) ;
}
while ( get_child_count ( ) ) {
Node * child = get_child ( 0 ) ;
remove_child ( child ) ;
if ( ! child - > is_owned_by_parent ( ) ) {
// add the custom children to the p_node
p_node - > add_child ( child ) ;
}
}
p_node - > set_owner ( owner ) ;
for ( int i = 0 ; i < owned . size ( ) ; i + + ) {
owned [ i ] - > set_owner ( p_node ) ;
}
for ( int i = 0 ; i < owned_by_owner . size ( ) ; i + + ) {
owned_by_owner [ i ] - > set_owner ( owner ) ;
}
p_node - > set_filename ( get_filename ( ) ) ;
for ( List < _NodeReplaceByPair > : : Element * E = replace_data . front ( ) ; E ; E = E - > next ( ) ) {
p_node - > set ( E - > get ( ) . name , E - > get ( ) . value ) ;
}
}
void Node : : _replace_connections_target ( Node * p_new_target ) {
List < Connection > cl ;
get_signals_connected_to_this ( & cl ) ;
for ( List < Connection > : : Element * E = cl . front ( ) ; E ; E = E - > next ( ) ) {
Connection & c = E - > get ( ) ;
if ( c . flags & CONNECT_PERSIST ) {
c . source - > disconnect ( c . signal , this , c . method ) ;
bool valid = p_new_target - > has_method ( c . method ) | | Ref < Script > ( p_new_target - > get_script ( ) ) . is_null ( ) | | Ref < Script > ( p_new_target - > get_script ( ) ) - > has_method ( c . method ) ;
ERR_CONTINUE_MSG ( ! valid , " Attempt to connect signal ' " + c . source - > get_class ( ) + " . " + c . signal + " ' to nonexistent method ' " + c . target - > get_class ( ) + " . " + c . method + " '. " ) ;
c . source - > connect ( c . signal , p_new_target , c . method , c . binds , c . flags ) ;
}
}
}
Vector < Variant > Node : : make_binds ( VARIANT_ARG_DECLARE ) {
Vector < Variant > ret ;
if ( p_arg1 . get_type ( ) = = Variant : : NIL ) {
return ret ;
} else {
ret . push_back ( p_arg1 ) ;
}
if ( p_arg2 . get_type ( ) = = Variant : : NIL ) {
return ret ;
} else {
ret . push_back ( p_arg2 ) ;
}
if ( p_arg3 . get_type ( ) = = Variant : : NIL ) {
return ret ;
} else {
ret . push_back ( p_arg3 ) ;
}
if ( p_arg4 . get_type ( ) = = Variant : : NIL ) {
return ret ;
} else {
ret . push_back ( p_arg4 ) ;
}
if ( p_arg5 . get_type ( ) = = Variant : : NIL ) {
return ret ;
} else {
ret . push_back ( p_arg5 ) ;
}
return ret ;
}
bool Node : : has_node_and_resource ( const NodePath & p_path ) const {
if ( ! has_node ( p_path ) ) {
return false ;
}
RES res ;
Vector < StringName > leftover_path ;
Node * node = get_node_and_resource ( p_path , res , leftover_path , false ) ;
return node ;
}
Array Node : : _get_node_and_resource ( const NodePath & p_path ) {
RES res ;
Vector < StringName > leftover_path ;
Node * node = get_node_and_resource ( p_path , res , leftover_path , false ) ;
Array result ;
if ( node ) {
result . push_back ( node ) ;
} else {
result . push_back ( Variant ( ) ) ;
}
if ( res . is_valid ( ) ) {
result . push_back ( res ) ;
} else {
result . push_back ( Variant ( ) ) ;
}
result . push_back ( NodePath ( Vector < StringName > ( ) , leftover_path , false ) ) ;
return result ;
}
Node * Node : : get_node_and_resource ( const NodePath & p_path , RES & r_res , Vector < StringName > & r_leftover_subpath , bool p_last_is_property ) const {
Node * node = get_node ( p_path ) ;
r_res = RES ( ) ;
r_leftover_subpath = Vector < StringName > ( ) ;
if ( ! node ) {
return nullptr ;
}
if ( p_path . get_subname_count ( ) ) {
int j = 0 ;
// If not p_last_is_property, we shouldn't consider the last one as part of the resource
for ( ; j < p_path . get_subname_count ( ) - ( int ) p_last_is_property ; j + + ) {
Variant new_res_v = j = = 0 ? node - > get ( p_path . get_subname ( j ) ) : r_res - > get ( p_path . get_subname ( j ) ) ;
if ( new_res_v . get_type ( ) = = Variant : : NIL ) { // Found nothing on that path
return nullptr ;
}
RES new_res = new_res_v ;
if ( new_res . is_null ( ) ) { // No longer a resource, assume property
break ;
}
r_res = new_res ;
}
for ( ; j < p_path . get_subname_count ( ) ; j + + ) {
// Put the rest of the subpath in the leftover path
r_leftover_subpath . push_back ( p_path . get_subname ( j ) ) ;
}
}
return node ;
}
void Node : : _set_tree ( SceneTree * p_tree ) {
SceneTree * tree_changed_a = nullptr ;
SceneTree * tree_changed_b = nullptr ;
//ERR_FAIL_COND(p_scene && data.parent && !data.parent->data.scene); //nobug if both are null
if ( data . tree ) {
_propagate_exit_tree ( ) ;
tree_changed_a = data . tree ;
}
data . tree = p_tree ;
if ( data . tree ) {
_propagate_enter_tree ( ) ;
if ( ! data . parent | | data . parent - > data . ready_notified ) { // No parent (root) or parent ready
_propagate_ready ( ) ; //reverse_notification(NOTIFICATION_READY);
}
tree_changed_b = data . tree ;
}
if ( tree_changed_a ) {
tree_changed_a - > tree_changed ( ) ;
}
if ( tree_changed_b ) {
tree_changed_b - > tree_changed ( ) ;
}
}
# ifdef DEBUG_ENABLED
static void _Node_debug_sn ( Object * p_obj ) {
Node * n = Object : : cast_to < Node > ( p_obj ) ;
if ( ! n ) {
return ;
}
if ( n - > is_inside_tree ( ) ) {
return ;
}
Node * p = n ;
while ( p - > get_parent ( ) ) {
p = p - > get_parent ( ) ;
}
String path ;
if ( p = = n ) {
path = n - > get_name ( ) ;
} else {
path = String ( p - > get_name ( ) ) + " / " + p - > get_path_to ( n ) ;
}
2021-03-18 20:46:24 +01:00
String script_file_string ;
if ( ! n - > get_script ( ) . is_null ( ) ) {
Ref < Script > script = n - > get_script ( ) ;
if ( script . is_valid ( ) ) {
script_file_string = " , Script: " + script - > get_path ( ) ;
}
}
print_line ( itos ( p_obj - > get_instance_id ( ) ) + " - Stray Node: " + path + " (Type: " + n - > get_class ( ) + script_file_string + " ) " ) ;
2022-03-15 13:29:32 +01:00
}
# endif // DEBUG_ENABLED
void Node : : _print_stray_nodes ( ) {
print_stray_nodes ( ) ;
}
void Node : : print_stray_nodes ( ) {
# ifdef DEBUG_ENABLED
ObjectDB : : debug_objects ( _Node_debug_sn ) ;
# endif
}
void Node : : queue_delete ( ) {
2022-10-25 03:31:56 +02:00
// There are users which instantiate multiple scene trees for their games.
// Use the node's own tree to handle its deletion when relevant.
2022-03-15 13:29:32 +01:00
if ( is_inside_tree ( ) ) {
get_tree ( ) - > queue_delete ( this ) ;
} else {
2022-10-25 03:31:56 +02:00
SceneTree * tree = SceneTree : : get_singleton ( ) ;
ERR_FAIL_NULL_MSG ( tree , " Can't queue free a node when no SceneTree is available. " ) ;
tree - > queue_delete ( this ) ;
2022-03-15 13:29:32 +01:00
}
}
Array Node : : _get_children ( ) const {
Array arr ;
int cc = get_child_count ( ) ;
arr . resize ( cc ) ;
for ( int i = 0 ; i < cc ; i + + ) {
arr [ i ] = get_child ( i ) ;
}
return arr ;
}
void Node : : set_import_path ( const NodePath & p_import_path ) {
# ifdef TOOLS_ENABLED
data . import_path = p_import_path ;
# endif
}
NodePath Node : : get_import_path ( ) const {
# ifdef TOOLS_ENABLED
return data . import_path ;
# else
return NodePath ( ) ;
# endif
}
static void _add_nodes_to_options ( const Node * p_base , const Node * p_node , List < String > * r_options ) {
# ifdef TOOLS_ENABLED
const String quote_style = EDITOR_DEF ( " text_editor/completion/use_single_quotes " , 0 ) ? " ' " : " \" " ;
# else
const String quote_style = " \" " ;
# endif
if ( p_node ! = p_base & & ! p_node - > get_owner ( ) ) {
return ;
}
String n = p_base - > get_path_to ( p_node ) ;
r_options - > push_back ( quote_style + n + quote_style ) ;
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
_add_nodes_to_options ( p_base , p_node - > get_child ( i ) , r_options ) ;
}
}
void Node : : get_argument_options ( const StringName & p_function , int p_idx , List < String > * r_options ) const {
String pf = p_function ;
if ( ( pf = = " has_node " | | pf = = " get_node " ) & & p_idx = = 0 ) {
_add_nodes_to_options ( this , this , r_options ) ;
}
Object : : get_argument_options ( p_function , p_idx , r_options ) ;
}
void Node : : clear_internal_tree_resource_paths ( ) {
clear_internal_resource_paths ( ) ;
2023-04-10 17:32:42 +02:00
for ( HashMap < StringName , Node * > : : Element * E = data . children . front ( ) ; E ; E = E - > next ) {
E - > value ( ) - > clear_internal_tree_resource_paths ( ) ;
2022-03-15 13:29:32 +01:00
}
}
String Node : : get_configuration_warning ( ) const {
if ( get_script_instance ( ) & & get_script_instance ( ) - > get_script ( ) . is_valid ( ) & &
get_script_instance ( ) - > get_script ( ) - > is_tool ( ) & & get_script_instance ( ) - > has_method ( " _get_configuration_warning " ) ) {
return get_script_instance ( ) - > call ( " _get_configuration_warning " ) ;
}
return String ( ) ;
}
void Node : : update_configuration_warning ( ) {
# ifdef TOOLS_ENABLED
if ( ! is_inside_tree ( ) ) {
return ;
}
if ( get_tree ( ) - > get_edited_scene_root ( ) & & ( get_tree ( ) - > get_edited_scene_root ( ) = = this | | get_tree ( ) - > get_edited_scene_root ( ) - > is_a_parent_of ( this ) ) ) {
get_tree ( ) - > emit_signal ( SceneStringNames : : get_singleton ( ) - > node_configuration_warning_changed , this ) ;
}
# endif
}
bool Node : : is_owned_by_parent ( ) const {
return data . parent_owned ;
}
void Node : : set_display_folded ( bool p_folded ) {
data . display_folded = p_folded ;
}
bool Node : : is_displayed_folded ( ) const {
return data . display_folded ;
}
void Node : : request_ready ( ) {
data . ready_first = true ;
}
void Node : : _bind_methods ( ) {
GLOBAL_DEF ( " node/name_num_separator " , 0 ) ;
ProjectSettings : : get_singleton ( ) - > set_custom_property_info ( " node/name_num_separator " , PropertyInfo ( Variant : : INT , " node/name_num_separator " , PROPERTY_HINT_ENUM , " None,Space,Underscore,Dash " ) ) ;
GLOBAL_DEF ( " node/name_casing " , NAME_CASING_PASCAL_CASE ) ;
ProjectSettings : : get_singleton ( ) - > set_custom_property_info ( " node/name_casing " , PropertyInfo ( Variant : : INT , " node/name_casing " , PROPERTY_HINT_ENUM , " PascalCase,camelCase,snake_case " ) ) ;
2022-10-12 18:02:26 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_child_below_node " , " node " , " child_node " , " force_readable_name " ) , & Node : : add_child_below_node , DEFVAL ( false ) ) ;
2022-03-15 13:29:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_name " , " name " ) , & Node : : set_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_name " ) , & Node : : get_name ) ;
2022-10-12 18:02:26 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_child " , " node " , " force_readable_name " ) , & Node : : add_child , DEFVAL ( false ) ) ;
2022-03-15 13:29:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " remove_child " , " node " ) , & Node : : remove_child ) ;
ClassDB : : bind_method ( D_METHOD ( " get_child_count " ) , & Node : : get_child_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_children " ) , & Node : : _get_children ) ;
ClassDB : : bind_method ( D_METHOD ( " get_child " , " idx " ) , & Node : : get_child ) ;
ClassDB : : bind_method ( D_METHOD ( " has_node " , " path " ) , & Node : : has_node ) ;
ClassDB : : bind_method ( D_METHOD ( " get_node " , " path " ) , & Node : : get_node ) ;
ClassDB : : bind_method ( D_METHOD ( " get_node_or_null " , " path " ) , & Node : : get_node_or_null ) ;
ClassDB : : bind_method ( D_METHOD ( " get_parent " ) , & Node : : get_parent ) ;
ClassDB : : bind_method ( D_METHOD ( " find_node " , " mask " , " recursive " , " owned " ) , & Node : : find_node , DEFVAL ( true ) , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " find_parent " , " mask " ) , & Node : : find_parent ) ;
ClassDB : : bind_method ( D_METHOD ( " has_node_and_resource " , " path " ) , & Node : : has_node_and_resource ) ;
ClassDB : : bind_method ( D_METHOD ( " get_node_and_resource " , " path " ) , & Node : : _get_node_and_resource ) ;
ClassDB : : bind_method ( D_METHOD ( " is_inside_tree " ) , & Node : : is_inside_tree ) ;
ClassDB : : bind_method ( D_METHOD ( " is_a_parent_of " , " node " ) , & Node : : is_a_parent_of ) ;
ClassDB : : bind_method ( D_METHOD ( " is_greater_than " , " node " ) , & Node : : is_greater_than ) ;
ClassDB : : bind_method ( D_METHOD ( " get_path " ) , & Node : : get_path ) ;
ClassDB : : bind_method ( D_METHOD ( " get_path_to " , " node " ) , & Node : : get_path_to ) ;
ClassDB : : bind_method ( D_METHOD ( " add_to_group " , " group " , " persistent " ) , & Node : : add_to_group , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_from_group " , " group " ) , & Node : : remove_from_group ) ;
ClassDB : : bind_method ( D_METHOD ( " is_in_group " , " group " ) , & Node : : is_in_group ) ;
ClassDB : : bind_method ( D_METHOD ( " move_child " , " child_node " , " to_position " ) , & Node : : move_child ) ;
ClassDB : : bind_method ( D_METHOD ( " get_groups " ) , & Node : : _get_groups ) ;
ClassDB : : bind_method ( D_METHOD ( " raise " ) , & Node : : raise ) ;
ClassDB : : bind_method ( D_METHOD ( " set_owner " , " owner " ) , & Node : : set_owner ) ;
ClassDB : : bind_method ( D_METHOD ( " get_owner " ) , & Node : : get_owner ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_and_skip " ) , & Node : : remove_and_skip ) ;
ClassDB : : bind_method ( D_METHOD ( " get_index " ) , & Node : : get_index ) ;
ClassDB : : bind_method ( D_METHOD ( " print_tree " ) , & Node : : print_tree ) ;
ClassDB : : bind_method ( D_METHOD ( " print_tree_pretty " ) , & Node : : print_tree_pretty ) ;
ClassDB : : bind_method ( D_METHOD ( " set_filename " , " filename " ) , & Node : : set_filename ) ;
ClassDB : : bind_method ( D_METHOD ( " get_filename " ) , & Node : : get_filename ) ;
ClassDB : : bind_method ( D_METHOD ( " propagate_notification " , " what " ) , & Node : : propagate_notification ) ;
ClassDB : : bind_method ( D_METHOD ( " propagate_call " , " method " , " args " , " parent_first " ) , & Node : : propagate_call , DEFVAL ( Array ( ) ) , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_physics_process " , " enable " ) , & Node : : set_physics_process ) ;
ClassDB : : bind_method ( D_METHOD ( " get_physics_process_delta_time " ) , & Node : : get_physics_process_delta_time ) ;
ClassDB : : bind_method ( D_METHOD ( " is_physics_processing " ) , & Node : : is_physics_processing ) ;
ClassDB : : bind_method ( D_METHOD ( " get_process_delta_time " ) , & Node : : get_process_delta_time ) ;
ClassDB : : bind_method ( D_METHOD ( " set_process " , " enable " ) , & Node : : set_process ) ;
ClassDB : : bind_method ( D_METHOD ( " set_process_priority " , " priority " ) , & Node : : set_process_priority ) ;
ClassDB : : bind_method ( D_METHOD ( " get_process_priority " ) , & Node : : get_process_priority ) ;
ClassDB : : bind_method ( D_METHOD ( " is_processing " ) , & Node : : is_processing ) ;
ClassDB : : bind_method ( D_METHOD ( " set_process_input " , " enable " ) , & Node : : set_process_input ) ;
ClassDB : : bind_method ( D_METHOD ( " is_processing_input " ) , & Node : : is_processing_input ) ;
ClassDB : : bind_method ( D_METHOD ( " set_process_unhandled_input " , " enable " ) , & Node : : set_process_unhandled_input ) ;
ClassDB : : bind_method ( D_METHOD ( " is_processing_unhandled_input " ) , & Node : : is_processing_unhandled_input ) ;
ClassDB : : bind_method ( D_METHOD ( " set_process_unhandled_key_input " , " enable " ) , & Node : : set_process_unhandled_key_input ) ;
ClassDB : : bind_method ( D_METHOD ( " is_processing_unhandled_key_input " ) , & Node : : is_processing_unhandled_key_input ) ;
ClassDB : : bind_method ( D_METHOD ( " set_pause_mode " , " mode " ) , & Node : : set_pause_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_pause_mode " ) , & Node : : get_pause_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " can_process " ) , & Node : : can_process ) ;
ClassDB : : bind_method ( D_METHOD ( " print_stray_nodes " ) , & Node : : _print_stray_nodes ) ;
ClassDB : : bind_method ( D_METHOD ( " get_position_in_parent " ) , & Node : : get_position_in_parent ) ;
ClassDB : : bind_method ( D_METHOD ( " set_display_folded " , " fold " ) , & Node : : set_display_folded ) ;
ClassDB : : bind_method ( D_METHOD ( " is_displayed_folded " ) , & Node : : is_displayed_folded ) ;
ClassDB : : bind_method ( D_METHOD ( " set_process_internal " , " enable " ) , & Node : : set_process_internal ) ;
ClassDB : : bind_method ( D_METHOD ( " is_processing_internal " ) , & Node : : is_processing_internal ) ;
ClassDB : : bind_method ( D_METHOD ( " set_physics_process_internal " , " enable " ) , & Node : : set_physics_process_internal ) ;
ClassDB : : bind_method ( D_METHOD ( " is_physics_processing_internal " ) , & Node : : is_physics_processing_internal ) ;
2022-07-27 15:16:37 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_physics_interpolation_mode " , " mode " ) , & Node : : set_physics_interpolation_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_physics_interpolation_mode " ) , & Node : : get_physics_interpolation_mode ) ;
2022-03-15 13:29:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " is_physics_interpolated " ) , & Node : : is_physics_interpolated ) ;
ClassDB : : bind_method ( D_METHOD ( " is_physics_interpolated_and_enabled " ) , & Node : : is_physics_interpolated_and_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " reset_physics_interpolation " ) , & Node : : reset_physics_interpolation ) ;
ClassDB : : bind_method ( D_METHOD ( " get_tree " ) , & Node : : get_tree ) ;
2022-07-27 13:37:17 +02:00
ClassDB : : bind_method ( D_METHOD ( " create_tween " ) , & Node : : create_tween ) ;
2022-03-15 13:29:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " duplicate " , " flags " ) , & Node : : duplicate , DEFVAL ( DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS ) ) ;
ClassDB : : bind_method ( D_METHOD ( " replace_by " , " node " , " keep_data " ) , & Node : : replace_by , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_scene_instance_load_placeholder " , " load_placeholder " ) , & Node : : set_scene_instance_load_placeholder ) ;
ClassDB : : bind_method ( D_METHOD ( " get_scene_instance_load_placeholder " ) , & Node : : get_scene_instance_load_placeholder ) ;
2021-08-23 04:18:29 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_editable_instance " , " node " , " is_editable " ) , & Node : : set_editable_instance ) ;
ClassDB : : bind_method ( D_METHOD ( " is_editable_instance " , " node " ) , & Node : : is_editable_instance ) ;
2022-03-15 13:29:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_viewport " ) , & Node : : get_viewport ) ;
2022-08-29 19:40:17 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_world " ) , & Node : : get_world ) ;
2022-03-15 13:29:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " queue_free " ) , & Node : : queue_delete ) ;
ClassDB : : bind_method ( D_METHOD ( " request_ready " ) , & Node : : request_ready ) ;
ClassDB : : bind_method ( D_METHOD ( " set_network_master " , " id " , " recursive " ) , & Node : : set_network_master , DEFVAL ( true ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_network_master " ) , & Node : : get_network_master ) ;
ClassDB : : bind_method ( D_METHOD ( " is_network_master " ) , & Node : : is_network_master ) ;
ClassDB : : bind_method ( D_METHOD ( " get_multiplayer " ) , & Node : : get_multiplayer ) ;
ClassDB : : bind_method ( D_METHOD ( " get_custom_multiplayer " ) , & Node : : get_custom_multiplayer ) ;
ClassDB : : bind_method ( D_METHOD ( " set_custom_multiplayer " , " api " ) , & Node : : set_custom_multiplayer ) ;
ClassDB : : bind_method ( D_METHOD ( " rpc_config " , " method " , " mode " ) , & Node : : rpc_config ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_editor_description " , " editor_description " ) , & Node : : set_editor_description ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_editor_description " ) , & Node : : get_editor_description ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " editor_description " , PROPERTY_HINT_MULTILINE_TEXT , " " , PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL ) , " _set_editor_description " , " _get_editor_description " ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_import_path " , " import_path " ) , & Node : : set_import_path ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_import_path " ) , & Node : : get_import_path ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : NODE_PATH , " _import_path " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ) , " _set_import_path " , " _get_import_path " ) ;
# ifdef TOOLS_ENABLED
ClassDB : : bind_method ( D_METHOD ( " _set_property_pinned " , " property " , " pinned " ) , & Node : : set_property_pinned ) ;
# endif
2022-08-19 00:36:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " has_network_peer " ) , & Node : : has_network_peer ) ;
ClassDB : : bind_method ( D_METHOD ( " is_network_server " ) , & Node : : is_network_server ) ;
ClassDB : : bind_method ( D_METHOD ( " is_network_client " ) , & Node : : is_network_client ) ;
2022-08-19 00:04:38 +02:00
ClassDB : : bind_method ( D_METHOD ( " sees_get " , " index " ) , & Node : : sees_get ) ;
ClassDB : : bind_method ( D_METHOD ( " sees_remove_index " , " index " ) , & Node : : sees_remove_index ) ;
ClassDB : : bind_method ( D_METHOD ( " sees_remove " , " entity " ) , & Node : : sees_remove ) ;
ClassDB : : bind_method ( D_METHOD ( " sees_add " , " entity " ) , & Node : : sees_add ) ;
ClassDB : : bind_method ( D_METHOD ( " sees_get_count " ) , & Node : : sees_get_count ) ;
ClassDB : : bind_method ( D_METHOD ( " seen_by_get " , " index " ) , & Node : : seen_by_get ) ;
ClassDB : : bind_method ( D_METHOD ( " seen_by_remove_index " , " index " ) , & Node : : seen_by_remove_index ) ;
ClassDB : : bind_method ( D_METHOD ( " seen_by_remove " , " entity " ) , & Node : : seen_by_remove ) ;
ClassDB : : bind_method ( D_METHOD ( " seen_by_add " , " entity " ) , & Node : : seen_by_add ) ;
ClassDB : : bind_method ( D_METHOD ( " seen_by_get_count " ) , & Node : : seen_by_get_count ) ;
2022-07-27 00:45:48 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_unique_name_in_owner " , " enable " ) , & Node : : set_unique_name_in_owner ) ;
ClassDB : : bind_method ( D_METHOD ( " is_unique_name_in_owner " ) , & Node : : is_unique_name_in_owner ) ;
2022-03-15 13:29:32 +01:00
{
MethodInfo mi ;
2022-03-21 21:29:06 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING_NAME , " method " ) ) ;
2022-03-15 13:29:32 +01:00
mi . name = " rpc " ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " rpc " , & Node : : _rpc_bind , mi ) ;
mi . name = " rpc_unreliable " ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " rpc_unreliable " , & Node : : _rpc_unreliable_bind , mi ) ;
2022-08-19 00:04:38 +02:00
mi . name = " vrpc " ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " vrpc " , & Node : : _vrpc_bind , mi ) ;
mi . name = " vrpc_unreliable " ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " vrpc_unreliable " , & Node : : _vrpc_bind , mi ) ;
2022-03-15 13:29:32 +01:00
mi . arguments . push_front ( PropertyInfo ( Variant : : INT , " peer_id " ) ) ;
mi . name = " rpc_id " ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " rpc_id " , & Node : : _rpc_id_bind , mi ) ;
mi . name = " rpc_unreliable_id " ;
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " rpc_unreliable_id " , & Node : : _rpc_unreliable_id_bind , mi ) ;
}
ClassDB : : bind_method ( D_METHOD ( " update_configuration_warning " ) , & Node : : update_configuration_warning ) ;
BIND_CONSTANT ( NOTIFICATION_ENTER_TREE ) ;
BIND_CONSTANT ( NOTIFICATION_EXIT_TREE ) ;
2023-04-10 15:55:34 +02:00
BIND_CONSTANT ( NOTIFICATION_CHILD_ORDER_CHANGED ) ;
2022-03-15 13:29:32 +01:00
BIND_CONSTANT ( NOTIFICATION_READY ) ;
BIND_CONSTANT ( NOTIFICATION_PAUSED ) ;
BIND_CONSTANT ( NOTIFICATION_UNPAUSED ) ;
BIND_CONSTANT ( NOTIFICATION_PHYSICS_PROCESS ) ;
BIND_CONSTANT ( NOTIFICATION_PROCESS ) ;
BIND_CONSTANT ( NOTIFICATION_PARENTED ) ;
BIND_CONSTANT ( NOTIFICATION_UNPARENTED ) ;
BIND_CONSTANT ( NOTIFICATION_INSTANCED ) ;
BIND_CONSTANT ( NOTIFICATION_DRAG_BEGIN ) ;
BIND_CONSTANT ( NOTIFICATION_DRAG_END ) ;
BIND_CONSTANT ( NOTIFICATION_PATH_CHANGED ) ;
BIND_CONSTANT ( NOTIFICATION_INTERNAL_PROCESS ) ;
BIND_CONSTANT ( NOTIFICATION_INTERNAL_PHYSICS_PROCESS ) ;
BIND_CONSTANT ( NOTIFICATION_POST_ENTER_TREE ) ;
BIND_CONSTANT ( NOTIFICATION_RESET_PHYSICS_INTERPOLATION ) ;
Ported from Godot4: New and improved IK system for Skeleton2D
This PR and commit adds a new IK system for 2D with the Skeleton2D node
that adds several new IK solvers, a way to control bones in a Skeleton2D
node similar to that in Skeleton3D. It also adds additional changes
and functionality.
This work was sponsored by GSoC 2020 and TwistedTwigleg.
Full list of changes:
* Adds a SkeletonModifier2D resource
* This resource is the base where all IK code is written and executed
* Has a function for clamping angles, since it is so commonly used
* Modifiers are unique when duplicated so it works with instancing
* Adds a SkeletonModifierStack2D resource
* This resource manages a series of SkeletonModification2Ds
* This is what the Skeleton2D directly interfaces with to make IK possible
* Adds SkeletonModifier2D resources for LookAt, CCDIK, FABRIK, Jiggle, and TwoBoneIK
* Each modification is in its own file
* There is also a SkeletonModifier2D resource that acts as a stack for using multiple stacks together
* Adds a PhysicalBone2D node
* Works similar to the PhysicalBone3D node, but uses a RigidBody2D node
* Changes to Skeleton2D listed below:
* Skeleton2D now holds a single SkeletonModificationStack2D for IK
* Skeleton2D now has a local_pose_override, which overrides the Bone2D position similar to how the overrides work in Skeleton3D
* Changes to Bone2D listed below:
* The default_length property has been changed to length. Length is the length of the bone to its child bone node
* New bone_angle property, which is the angle the bone has to its first child bone node
* Bone2D caches its transform when not modified by IK for IK interpolation purposes
* Bone2D draws its own editor gizmo, though this is stated to change in the future
* Changes to CanvasItemEditor listed below:
* Bone2D gizmo drawing code removed
* The 2D IK code is removed. Now Bone2D is the only bone system for 2D
* Transform2D now has a looking_at function for rotating to face a position
* Two new node notifications: NOTIFICATION_EDITOR_PRE_SAVE and NOTIFICATION_EDITOR_POST_SAVE
* These notifications only are called in the editor right before and after saving a scene
* Needed for not saving the IK position when executing IK in the editor
* Documentation for all the changes listed above.
- TwistedTwigleg
https://github.com/godotengine/godot/commit/8aa3c2f0918707c2243b86ac53c02c27ceb9a266
2022-08-18 18:47:30 +02:00
BIND_CONSTANT ( NOTIFICATION_EDITOR_PRE_SAVE ) ;
BIND_CONSTANT ( NOTIFICATION_EDITOR_POST_SAVE ) ;
2022-03-15 13:29:32 +01:00
BIND_CONSTANT ( NOTIFICATION_WM_MOUSE_ENTER ) ;
BIND_CONSTANT ( NOTIFICATION_WM_MOUSE_EXIT ) ;
BIND_CONSTANT ( NOTIFICATION_WM_FOCUS_IN ) ;
BIND_CONSTANT ( NOTIFICATION_WM_FOCUS_OUT ) ;
BIND_CONSTANT ( NOTIFICATION_WM_QUIT_REQUEST ) ;
BIND_CONSTANT ( NOTIFICATION_WM_GO_BACK_REQUEST ) ;
BIND_CONSTANT ( NOTIFICATION_WM_UNFOCUS_REQUEST ) ;
BIND_CONSTANT ( NOTIFICATION_OS_MEMORY_WARNING ) ;
BIND_CONSTANT ( NOTIFICATION_TRANSLATION_CHANGED ) ;
BIND_CONSTANT ( NOTIFICATION_WM_ABOUT ) ;
BIND_CONSTANT ( NOTIFICATION_CRASH ) ;
BIND_CONSTANT ( NOTIFICATION_OS_IME_UPDATE ) ;
BIND_CONSTANT ( NOTIFICATION_APP_RESUMED ) ;
BIND_CONSTANT ( NOTIFICATION_APP_PAUSED ) ;
BIND_ENUM_CONSTANT ( PAUSE_MODE_INHERIT ) ;
BIND_ENUM_CONSTANT ( PAUSE_MODE_STOP ) ;
BIND_ENUM_CONSTANT ( PAUSE_MODE_PROCESS ) ;
2022-07-27 15:16:37 +02:00
BIND_ENUM_CONSTANT ( PHYSICS_INTERPOLATION_MODE_INHERIT ) ;
BIND_ENUM_CONSTANT ( PHYSICS_INTERPOLATION_MODE_OFF ) ;
BIND_ENUM_CONSTANT ( PHYSICS_INTERPOLATION_MODE_ON ) ;
2022-03-15 13:29:32 +01:00
BIND_ENUM_CONSTANT ( DUPLICATE_SIGNALS ) ;
BIND_ENUM_CONSTANT ( DUPLICATE_GROUPS ) ;
BIND_ENUM_CONSTANT ( DUPLICATE_SCRIPTS ) ;
BIND_ENUM_CONSTANT ( DUPLICATE_USE_INSTANCING ) ;
ADD_SIGNAL ( MethodInfo ( " ready " ) ) ;
ADD_SIGNAL ( MethodInfo ( " renamed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " tree_entered " ) ) ;
ADD_SIGNAL ( MethodInfo ( " tree_exiting " ) ) ;
ADD_SIGNAL ( MethodInfo ( " tree_exited " ) ) ;
ADD_SIGNAL ( MethodInfo ( " child_entered_tree " , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_DEFAULT , " Node " ) ) ) ;
2022-06-20 09:53:44 +02:00
ADD_SIGNAL ( MethodInfo ( " child_exiting_tree " , PropertyInfo ( Variant : : OBJECT , " node " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_DEFAULT , " Node " ) ) ) ;
2023-04-10 15:55:34 +02:00
ADD_SIGNAL ( MethodInfo ( " child_order_changed " ) ) ;
2022-03-15 13:29:32 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " pause_mode " , PROPERTY_HINT_ENUM , " Inherit,Stop,Process " ) , " set_pause_mode " , " get_pause_mode " ) ;
2022-07-27 15:16:37 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " physics_interpolation_mode " , PROPERTY_HINT_ENUM , " Inherit,Off,On " ) , " set_physics_interpolation_mode " , " get_physics_interpolation_mode " ) ;
2022-03-15 13:29:32 +01:00
# ifdef ENABLE_DEPRECATED
//no longer exists, but remains for compatibility (keep previous scenes folded
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " editor/display_folded " , PROPERTY_HINT_NONE , " " , 0 ) , " set_display_folded " , " is_displayed_folded " ) ;
# endif
2022-03-21 21:29:06 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING_NAME , " name " , PROPERTY_HINT_NONE , " " , 0 ) , " set_name " , " get_name " ) ;
2022-07-27 00:45:48 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " unique_name_in_owner " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR ) , " set_unique_name_in_owner " , " is_unique_name_in_owner " ) ;
2022-03-15 13:29:32 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " filename " , PROPERTY_HINT_NONE , " " , 0 ) , " set_filename " , " get_filename " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " owner " , PROPERTY_HINT_RESOURCE_TYPE , " Node " , 0 ) , " set_owner " , " get_owner " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " multiplayer " , PROPERTY_HINT_RESOURCE_TYPE , " MultiplayerAPI " , 0 ) , " " , " get_multiplayer " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " custom_multiplayer " , PROPERTY_HINT_RESOURCE_TYPE , " MultiplayerAPI " , 0 ) , " set_custom_multiplayer " , " get_custom_multiplayer " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " process_priority " ) , " set_process_priority " , " get_process_priority " ) ;
BIND_VMETHOD ( MethodInfo ( " _process " , PropertyInfo ( Variant : : REAL , " delta " ) ) ) ;
BIND_VMETHOD ( MethodInfo ( " _physics_process " , PropertyInfo ( Variant : : REAL , " delta " ) ) ) ;
BIND_VMETHOD ( MethodInfo ( " _enter_tree " ) ) ;
BIND_VMETHOD ( MethodInfo ( " _exit_tree " ) ) ;
BIND_VMETHOD ( MethodInfo ( " _ready " ) ) ;
BIND_VMETHOD ( MethodInfo ( " _input " , PropertyInfo ( Variant : : OBJECT , " event " , PROPERTY_HINT_RESOURCE_TYPE , " InputEvent " ) ) ) ;
BIND_VMETHOD ( MethodInfo ( " _unhandled_input " , PropertyInfo ( Variant : : OBJECT , " event " , PROPERTY_HINT_RESOURCE_TYPE , " InputEvent " ) ) ) ;
BIND_VMETHOD ( MethodInfo ( " _unhandled_key_input " , PropertyInfo ( Variant : : OBJECT , " event " , PROPERTY_HINT_RESOURCE_TYPE , " InputEventKey " ) ) ) ;
BIND_VMETHOD ( MethodInfo ( Variant : : STRING , " _get_configuration_warning " ) ) ;
}
String Node : : _get_name_num_separator ( ) {
switch ( ProjectSettings : : get_singleton ( ) - > get ( " node/name_num_separator " ) . operator int ( ) ) {
case 0 :
return " " ;
case 1 :
return " " ;
case 2 :
return " _ " ;
case 3 :
return " - " ;
}
return " " ;
}
Node : : Node ( ) {
data . pos = - 1 ;
data . depth = - 1 ;
data . blocked = 0 ;
data . parent = nullptr ;
data . tree = nullptr ;
data . physics_process = false ;
data . idle_process = false ;
data . process_priority = 0 ;
data . physics_process_internal = false ;
data . idle_process_internal = false ;
data . inside_tree = false ;
data . ready_notified = false ;
data . physics_interpolated = true ;
2022-07-27 17:51:39 +02:00
data . physics_interpolation_reset_requested = false ;
2022-03-15 13:29:32 +01:00
data . physics_interpolated_client_side = false ;
data . use_identity_transform = false ;
data . owner = nullptr ;
data . OW = nullptr ;
data . input = false ;
data . unhandled_input = false ;
data . unhandled_key_input = false ;
data . pause_mode = PAUSE_MODE_INHERIT ;
2022-07-27 15:16:37 +02:00
data . physics_interpolation_mode = PHYSICS_INTERPOLATION_MODE_INHERIT ;
2022-03-15 13:29:32 +01:00
data . pause_owner = nullptr ;
data . network_master = 1 ; //server by default
data . path_cache = nullptr ;
data . parent_owned = false ;
data . in_constructor = true ;
data . viewport = nullptr ;
2022-08-29 19:40:17 +02:00
data . world = nullptr ;
2022-03-15 13:29:32 +01:00
data . use_placeholder = false ;
data . display_folded = false ;
data . ready_first = true ;
data . editable_instance = false ;
orphan_node_count + + ;
}
Node : : ~ Node ( ) {
data . grouped . clear ( ) ;
data . owned . clear ( ) ;
data . children . clear ( ) ;
2023-04-10 17:32:42 +02:00
data . children_cache . clear ( ) ;
2022-03-15 13:29:32 +01:00
2022-08-19 00:04:38 +02:00
data . _sees . clear ( ) ;
data . _seen_by . clear ( ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND ( data . parent ) ;
ERR_FAIL_COND ( data . children . size ( ) ) ;
2023-04-10 17:32:42 +02:00
ERR_FAIL_COND ( data . children_cache . size ( ) ) ;
2022-03-15 13:29:32 +01:00
orphan_node_count - - ;
}
////////////////////////////////