diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 49a7248817..0a7b78fc2c 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -39,6 +39,7 @@ #include "core/object/script_language.h" #include "core/templates/local_vector.h" #include "core/variant/callable_bind.h" +#include "core/variant/container_type_validate.h" #include "scene/2d/node_2d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" @@ -80,6 +81,85 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { return pinned; } +Variant SceneState::_duplicate_recursive(const Variant &p_variant, HashMap, Ref>> &p_remap_cache, const Variant &p_fallback, Node *p_for_scene) { + switch (p_variant.get_type()) { + case Variant::OBJECT: { + // The local-to-scene subresource instance is preserved, thus maintaining the previous sharing relationship. + // This is mainly used when the sub-scene root is reset in the main scene. + Ref sub_res_of_from = p_variant; + if (sub_res_of_from.is_valid() && sub_res_of_from->is_local_to_scene()) { + return get_remap_resource(sub_res_of_from, p_remap_cache, p_fallback, p_for_scene); + } + } break; + case Variant::ARRAY: { + const Array &src = p_variant; + const Array &fallback = p_fallback; + + bool has_fallback = true; + Array dst; + if (src.is_typed()) { + const ContainerType &scr_type = src.get_element_type(); + dst.set_typed(scr_type); + has_fallback = false; + if (fallback.is_typed()) { + const ContainerType &fallback_type = fallback.get_element_type(); + has_fallback = + scr_type.builtin_type == fallback_type.builtin_type && + scr_type.class_name == fallback_type.class_name && + scr_type.script == fallback_type.script; + } + } + dst.resize(src.size()); + for (int i = 0; i < src.size(); i++) { + Variant ele_fallback; + if (has_fallback && fallback.size() > i) { + ele_fallback = fallback[i]; + } + dst[i] = _duplicate_recursive(src[i], p_remap_cache, ele_fallback, p_for_scene); + } + return dst; + } break; + case Variant::DICTIONARY: { + const Dictionary &src = p_variant; + const Dictionary &fallback = p_fallback; + + bool has_fallback = true; + Dictionary dst; + if (src.is_typed()) { + dst.set_typed(src.get_typed_key_builtin(), src.get_typed_key_class_name(), src.get_typed_key_script(), src.get_typed_value_builtin(), src.get_typed_value_class_name(), src.get_typed_value_script()); + has_fallback = false; + if (fallback.is_typed()) { + has_fallback = + src.get_typed_key_builtin() == fallback.get_typed_key_builtin() && + src.get_typed_key_class_name() == fallback.get_typed_key_class_name() && + src.get_typed_key_script() == fallback.get_typed_key_script() && + src.get_typed_value_builtin() == fallback.get_typed_value_builtin() && + src.get_typed_value_class_name() == fallback.get_typed_value_class_name() && + src.get_typed_value_script() == fallback.get_typed_value_script(); + } + } + + for (const KeyValue &KV : src) { + const Variant &k = KV.key; + const Variant &v = KV.value; + Variant val_fallback; + // FIXME: as both `src` and `fallback` are remapped values, so if the local-to-scene + // resource is used as the key, it is difficult to find its fallback value. + if (has_fallback && fallback.has(k)) { + val_fallback = fallback[k]; + } + dst.set( + _duplicate_recursive(k, p_remap_cache, Variant(), p_for_scene), + _duplicate_recursive(v, p_remap_cache, val_fallback, p_for_scene)); + } + return dst; + } break; + default: { + } + } + return p_variant; +} + Ref SceneState::get_remap_resource(const Ref &p_resource, HashMap, Ref>> &remap_cache, const Ref &p_fallback, Node *p_for_scene) { ERR_FAIL_COND_V(p_resource.is_null(), Ref()); @@ -114,14 +194,7 @@ Ref SceneState::get_remap_resource(const Ref &p_resource, Ha continue; // Do not change path. } - Variant value = p_resource->get(E.name); - - // The local-to-scene subresource instance is preserved, thus maintaining the previous sharing relationship. - // This is mainly used when the sub-scene root is reset in the main scene. - Ref sub_res_of_from = value; - if (sub_res_of_from.is_valid() && sub_res_of_from->is_local_to_scene()) { - value = get_remap_resource(sub_res_of_from, remap_cache, p_fallback->get(E.name), p_fallback->get_local_scene()); - } + Variant value = _duplicate_recursive(p_resource->get(E.name), remap_cache, p_fallback->get(E.name), p_for_scene); p_fallback->set(E.name, value); } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index f95b4bf313..70253c47c6 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -107,6 +107,8 @@ class SceneState : public RefCounted { Node *_recover_node_path_index(Node *p_base, int p_idx) const; + static Variant _duplicate_recursive(const Variant &p_variant, HashMap, Ref>> &p_remap_cache, const Variant &p_fallback, Node *p_for_scene); + #ifdef TOOLS_ENABLED public: typedef void (*InstantiationWarningNotify)(const String &p_warning);