2023-01-05 12:25:55 +00:00
/**************************************************************************/
/* instance_placeholder.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2015-10-16 22:11:23 +00:00
# include "instance_placeholder.h"
2018-09-11 16:13:45 +00:00
# include "core/io/resource_loader.h"
2017-03-05 15:44:50 +00:00
# include "scene/resources/packed_scene.h"
2015-10-16 22:11:23 +00:00
2017-03-05 15:44:50 +00:00
bool InstancePlaceholder : : _set ( const StringName & p_name , const Variant & p_value ) {
2015-10-16 22:11:23 +00:00
PropSet ps ;
2017-03-05 15:44:50 +00:00
ps . name = p_name ;
ps . value = p_value ;
2015-10-16 22:11:23 +00:00
stored_values . push_back ( ps ) ;
return true ;
}
2017-03-05 15:44:50 +00:00
bool InstancePlaceholder : : _get ( const StringName & p_name , Variant & r_ret ) const {
2021-07-16 03:45:57 +00:00
for ( const PropSet & E : stored_values ) {
if ( E . name = = p_name ) {
r_ret = E . value ;
2016-01-22 22:36:40 +00:00
return true ;
}
}
2015-10-16 22:11:23 +00:00
return false ;
}
2018-03-06 01:57:17 +00:00
2017-03-05 15:44:50 +00:00
void InstancePlaceholder : : _get_property_list ( List < PropertyInfo > * p_list ) const {
2021-07-16 03:45:57 +00:00
for ( const PropSet & E : stored_values ) {
2016-01-22 22:36:40 +00:00
PropertyInfo pi ;
2021-07-16 03:45:57 +00:00
pi . name = E . name ;
pi . type = E . value . get_type ( ) ;
2017-03-05 15:44:50 +00:00
pi . usage = PROPERTY_USAGE_STORAGE ;
2015-10-16 22:11:23 +00:00
2016-01-22 22:36:40 +00:00
p_list - > push_back ( pi ) ;
}
2015-10-16 22:11:23 +00:00
}
2017-03-05 15:44:50 +00:00
void InstancePlaceholder : : set_instance_path ( const String & p_name ) {
path = p_name ;
2015-10-16 22:11:23 +00:00
}
2015-11-13 23:56:44 +00:00
String InstancePlaceholder : : get_instance_path ( ) const {
2015-10-16 22:11:23 +00:00
return path ;
}
2018-03-06 01:57:17 +00:00
Node * InstancePlaceholder : : create_instance ( bool p_replace , const Ref < PackedScene > & p_custom_scene ) {
2020-04-01 23:20:12 +00:00
ERR_FAIL_COND_V ( ! is_inside_tree ( ) , nullptr ) ;
2015-10-16 22:11:23 +00:00
Node * base = get_parent ( ) ;
2020-05-14 14:41:43 +00:00
if ( ! base ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2020-05-14 14:41:43 +00:00
}
2015-10-16 22:11:23 +00:00
Ref < PackedScene > ps ;
2020-05-14 14:41:43 +00:00
if ( p_custom_scene . is_valid ( ) ) {
2015-10-16 22:11:23 +00:00
ps = p_custom_scene ;
2020-05-14 14:41:43 +00:00
} else {
2017-03-05 15:44:50 +00:00
ps = ResourceLoader : : load ( path , " PackedScene " ) ;
2020-05-14 14:41:43 +00:00
}
2015-10-16 22:11:23 +00:00
2020-05-14 14:41:43 +00:00
if ( ! ps . is_valid ( ) ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2020-05-14 14:41:43 +00:00
}
2024-04-04 15:10:34 +00:00
Node * instance = ps - > instantiate ( ) ;
if ( ! instance ) {
2020-04-01 23:20:12 +00:00
return nullptr ;
2020-05-14 14:41:43 +00:00
}
2024-04-04 15:10:34 +00:00
instance - > set_name ( get_name ( ) ) ;
instance - > set_multiplayer_authority ( get_multiplayer_authority ( ) ) ;
2020-04-05 23:06:10 +00:00
int pos = get_index ( ) ;
2015-10-16 22:11:23 +00:00
2021-07-24 13:46:25 +00:00
for ( const PropSet & E : stored_values ) {
2024-04-04 15:10:34 +00:00
set_value_on_instance ( this , instance , E ) ;
2015-10-16 22:11:23 +00:00
}
2018-03-06 01:57:17 +00:00
if ( p_replace ) {
2022-10-24 21:07:02 +00:00
queue_free ( ) ;
2018-03-06 01:57:17 +00:00
base - > remove_child ( this ) ;
}
2015-10-16 22:11:23 +00:00
2024-04-04 15:10:34 +00:00
base - > add_child ( instance ) ;
base - > move_child ( instance , pos ) ;
2018-03-06 01:57:17 +00:00
2024-04-04 15:10:34 +00:00
return instance ;
}
// This method will attempt to set the correct values on the placeholder instance
// for regular types this is trivial and unnecessary.
// For nodes however this becomes a bit tricky because they might now have existed until the instantiation,
// so this method will try to find the correct nodes and resolve them.
void InstancePlaceholder : : set_value_on_instance ( InstancePlaceholder * p_placeholder , Node * p_instance , const PropSet & p_set ) {
bool is_valid ;
// If we don't have any info, we can't do anything,
// so try setting the value directly.
Variant current = p_instance - > get ( p_set . name , & is_valid ) ;
if ( ! is_valid ) {
p_instance - > set ( p_set . name , p_set . value , & is_valid ) ;
return ;
}
Variant : : Type current_type = current . get_type ( ) ;
Variant : : Type placeholder_type = p_set . value . get_type ( ) ;
// Arrays are a special case, because their containing type might be different.
if ( current_type ! = Variant : : Type : : ARRAY ) {
// Check if the variant types match.
if ( Variant : : evaluate ( Variant : : OP_EQUAL , current_type , placeholder_type ) ) {
p_instance - > set ( p_set . name , p_set . value , & is_valid ) ;
if ( is_valid ) {
return ;
}
// Types match but setting failed? This is strange, so let's print a warning!
WARN_PRINT ( vformat ( " Property '%s' with type '%s' could not be set when creating instance of '%s'. " , p_set . name , Variant : : get_type_name ( current_type ) , p_placeholder - > get_name ( ) ) ) ;
return ;
}
} else {
// We are dealing with an Array.
// Let's check if the subtype of the array matches first.
// This is needed because the set method of ScriptInstance checks for type,
// but the ClassDB set method doesn't! So we cannot reliably know what actually happens.
Array current_array = current ;
Array placeholder_array = p_set . value ;
if ( current_array . is_same_typed ( placeholder_array ) ) {
p_instance - > set ( p_set . name , p_set . value , & is_valid ) ;
if ( is_valid ) {
return ;
}
// Internal array types match but setting failed? This is strange, so let's print a warning!
WARN_PRINT ( vformat ( " Array Property '%s' with type '%s' could not be set when creating instance of '%s'. " , p_set . name , Variant : : get_type_name ( Variant : : Type ( current_array . get_typed_builtin ( ) ) ) , p_placeholder - > get_name ( ) ) ) ;
}
// Arrays are not the same internal type. This should be happening because we have a NodePath Array,
// but the instance wants a Node Array.
}
switch ( current_type ) {
case Variant : : Type : : NIL :
if ( placeholder_type ! = Variant : : Type : : NODE_PATH ) {
break ;
}
// If it's nil but we have a NodePath, we guess what works.
p_instance - > set ( p_set . name , p_set . value , & is_valid ) ;
if ( is_valid ) {
break ;
}
p_instance - > set ( p_set . name , try_get_node ( p_placeholder , p_instance , p_set . value ) , & is_valid ) ;
break ;
case Variant : : Type : : OBJECT :
if ( placeholder_type ! = Variant : : Type : : NODE_PATH ) {
break ;
}
// Easiest case, we want a node, but we have a deferred NodePath.
p_instance - > set ( p_set . name , try_get_node ( p_placeholder , p_instance , p_set . value ) ) ;
break ;
case Variant : : Type : : ARRAY : {
// If we have reached here it means our array types don't match,
// so we will convert the placeholder array into the correct type
// and resolve nodes if necessary.
Array current_array = current ;
Array converted_array ;
Array placeholder_array = p_set . value ;
converted_array = current_array . duplicate ( ) ;
converted_array . resize ( placeholder_array . size ( ) ) ;
if ( Variant : : evaluate ( Variant : : OP_EQUAL , current_array . get_typed_builtin ( ) , Variant : : Type : : NODE_PATH ) ) {
// We want a typed NodePath array.
for ( int i = 0 ; i < placeholder_array . size ( ) ; i + + ) {
converted_array . set ( i , placeholder_array [ i ] ) ;
}
} else {
// We want Nodes, convert NodePaths.
for ( int i = 0 ; i < placeholder_array . size ( ) ; i + + ) {
converted_array . set ( i , try_get_node ( p_placeholder , p_instance , placeholder_array [ i ] ) ) ;
}
}
p_instance - > set ( p_set . name , converted_array , & is_valid ) ;
if ( ! is_valid ) {
WARN_PRINT ( vformat ( " Property '%s' with type '%s' could not be set when creating instance of '%s'. " , p_set . name , Variant : : get_type_name ( current_type ) , p_placeholder - > get_name ( ) ) ) ;
}
break ;
}
default :
WARN_PRINT ( vformat ( " Property '%s' with type '%s' could not be set when creating instance of '%s'. " , p_set . name , Variant : : get_type_name ( current_type ) , p_placeholder - > get_name ( ) ) ) ;
break ;
}
}
Node * InstancePlaceholder : : try_get_node ( InstancePlaceholder * p_placeholder , Node * p_instance , const NodePath & p_path ) {
// First try to resolve internally,
// if that fails try resolving externally.
Node * node = p_instance - > get_node_or_null ( p_path ) ;
if ( node = = nullptr ) {
node = p_placeholder - > get_node_or_null ( p_path ) ;
}
return node ;
2018-03-06 01:57:17 +00:00
}
2016-11-08 20:40:46 +00:00
Dictionary InstancePlaceholder : : get_stored_values ( bool p_with_order ) {
Dictionary ret ;
2020-02-17 21:06:54 +00:00
PackedStringArray order ;
2016-11-08 20:40:46 +00:00
2021-07-24 13:46:25 +00:00
for ( const PropSet & E : stored_values ) {
2021-07-16 03:45:57 +00:00
ret [ E . name ] = E . value ;
2020-05-14 14:41:43 +00:00
if ( p_with_order ) {
2021-07-16 03:45:57 +00:00
order . push_back ( E . name ) ;
2020-05-14 14:41:43 +00:00
}
2016-11-08 20:40:46 +00:00
} ;
2020-05-14 14:41:43 +00:00
if ( p_with_order ) {
2016-11-08 20:40:46 +00:00
ret [ " .order " ] = order ;
2020-05-14 14:41:43 +00:00
}
2016-11-08 20:40:46 +00:00
return ret ;
} ;
2015-10-16 22:11:23 +00:00
void InstancePlaceholder : : _bind_methods ( ) {
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_stored_values " , " with_order " ) , & InstancePlaceholder : : get_stored_values , DEFVAL ( false ) ) ;
2018-03-06 01:57:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " create_instance " , " replace " , " custom_scene " ) , & InstancePlaceholder : : create_instance , DEFVAL ( false ) , DEFVAL ( Variant ( ) ) ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_instance_path " ) , & InstancePlaceholder : : get_instance_path ) ;
2015-10-16 22:11:23 +00:00
}
InstancePlaceholder : : InstancePlaceholder ( ) {
}