stick-the-quick/autoload/MapPreinit/MapPreinit.gd

927 lines
32 KiB
GDScript

extends Node
const DEFAULT_LIQUID_DEPTH: float = 32.0
var _convex_decomposition_settings: MeshConvexDecompositionSettings
func _ready() -> void:
_convex_decomposition_settings = MeshConvexDecompositionSettings.new()
_convex_decomposition_settings.max_concavity = 0.0
_convex_decomposition_settings.max_convex_hulls = 32
_convex_decomposition_settings.resolution = 100000
func setup(scene: Node) -> Object:
#print(scene.get_meta(&'extras'))
if scene:
var meshes: Array[Mesh] = []
var mats: Array[Material] = []
var nodes_by_unique_name := {}
print("===Map preinit for %s===" % scene)
print("---Setting up nodes---")
_setup_nodes(scene, scene, meshes, mats, nodes_by_unique_name)
print("---Applying shaders---")
_apply_shaders(meshes, mats)
print("---Applying properties---")
_apply_properties(scene, scene, nodes_by_unique_name)
print("===Map preinit for %s complete===" % scene)
#scene.print_tree_pretty()
return scene
func _setup_nodes(
what: Node,
root: Node,
meshes: Array[Mesh],
mats: Array[Material],
nodes_by_unique_name: Dictionary
) -> void:
for child in what.get_children():
_setup_nodes(child, root, meshes, mats, nodes_by_unique_name)
_setup_node(what, root, meshes, mats, nodes_by_unique_name)
func _record_mesh_and_mats(
what: Node, root: Node,
mesh: Mesh, meshes: Array[Mesh], mats: Array[Material]
) -> void:
if mesh and not (mesh in meshes):
#print((
#"Recording mesh %s and its materials " +
#"for use during applying shaders"
#) % mesh)
meshes.push_back(mesh)
for i in mesh.get_surface_count():
var mat := mesh.surface_get_material(i)
if mat and not (mat in mats):
#print("Recording material: %s" % mat)
mats.push_back(mat)
var meta := mat.get_meta(&'extras', {}) as Dictionary
var known_surfaces := root.get_meta(&'extras', {}).get(
root.get_path_to(what), {}
).get(&'surfaces', []) as Array
if known_surfaces.size() > i:
meta.merge(known_surfaces[i], true)
mat.set_meta(&'extras', meta)
func _setup_node(
what: Node,
root: Node,
meshes: Array[Mesh],
mats: Array[Material],
nodes_by_unique_name: Dictionary
) -> void:
#print("Setting up node %s" % what)
var unique_name := what.name
var replacement: Node = null
var discard := false
var recursive_discard := false
var extras := {}
var all_extras := root.get_meta(&'extras', {}) as Dictionary
var path := root.get_path_to(what)
var meta := all_extras.get(path, {}) as Dictionary
extras.merge(meta.get(&'mesh', {}), true)
extras.merge(meta.get(&'node', {}), true)
what.set_meta(&'extras', extras)
var node_type := &''
var path_prefix := ""
#print("Assessing node category...")
if extras.has(&'SpawnGroup'):
match extras.SpawnGroup:
&'conditional': node_type = &'ConditionalSpawnGroup'
&'deferred': node_type = &'DeferredSpawnGroup'
&'spawner': node_type = &'Spawner'
elif extras.has(&'NPC'):
#print("Node is npc: %s" % extras.NPC)
node_type = &'NPC'
path_prefix = "res://characters/npcs/%s/%s" % [extras.NPC, extras.NPC]
elif extras.has(&'Enemy'):
#print("Node is enemy: %s" % extras.Enemy)
node_type = &'Enemy'
path_prefix = "res://characters/enemies/%s/%s" % [
extras.Enemy, extras.Enemy
]
elif extras.has(&'Liquid'):
#print("Node is liquid: %s" % extras.Liquid)
node_type = &'Liquid'
path_prefix = "res://zones/Liquid/descriptors/%s" % extras.Liquid
elif extras.has(&'Object'):
#print("Node is game-object: %s" % extras.Object)
node_type = &'Object'
path_prefix = "res://objects/%s/%s" % [extras.Object, extras.Object]
elif extras.has(&'Path'):
#print("Node is path: %s" % extras.Path)
node_type = &'Path'
elif extras.has(&'VisualLiquid'):
#print("Node is visual liquid: %s" % extras.VisualLiquid)
node_type = &'VisualLiquid'
path_prefix = "res://zones/Liquid/descriptors/%s" % extras.VisualLiquid
elif extras.has(&'Zone'):
#print("Node is zone: %s" % extras.Zone)
node_type = &'Zone'
path_prefix = "res://zones/%s/%s" % [extras.Zone, extras.Zone]
elif extras.has(&'Intangible') and not extras.Intangible and (
what is Node3D and not (what is MeshInstance3D)
):
#print("Node is invisible wall")
node_type = &'InvisibleWall'
else:
#print("Node is uncategorized; no special setup needed")
pass
if not node_type.is_empty():
print("Setting up node %s as %s%s" % [
root.get_path_to(what), node_type,
(":%s" % extras.get(node_type)) if extras.has(node_type) else ""
])
var script_path: String = path_prefix + ".gd"
var scene_path: String = path_prefix + ".tscn"
var resource_path: String = path_prefix + ".tres"
var instantiated_from_script := false
#print("Doing node-category-specific setup:")
match node_type:
&'':
#print("None needed; to reiterate, node is uncategorized")
pass
&'ConditionalSpawnGroup':
#print("Instantiating ConditionalSpawnGroup")
replacement = ConditionalSpawnGroup.new()
#print("Instantiated: %s" % replacement)
&'DeferredSpawnGroup':
replacement = DeferredSpawnGroup.new()
&'InvisibleWall':
#print("Instantiating StaticBody3D")
replacement = StaticBody3D.new()
#print("Instantiated: %s" % replacement)
&'Liquid':
#print("Instantiating Liquid node")
replacement = load("res://zones/Liquid/Liquid.tscn").instantiate()
#print("Instantiated: %s" % replacement)
#print(
#"Trying to load LiquidDescriptor from %s" %
#resource_path
#)
if ResourceLoader.exists(resource_path):
replacement.descriptor = load(resource_path)
#print(
#"Loaded LiquidDescriptor: %s" %
#replacement.descriptor
#)
else:
#print("!!!LiquidDescriptor does not exist!!!")
push_error(
"LiquidDescriptor @ %s not found" % resource_path
)
&'Path':
if what is MeshInstance3D:
#print("Node stands in for a Curve3D, " +
#"which is a Resource; " +
#"actual node will be discarded")
discard = true
#print("Instantiating Curve3D")
var curve := Curve3D.new()
# Might try to switch to Hobby algorithm later, not sure
#print("Building curve from vertices of surface 0")
for vertex in (
what.mesh.surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
):
#print("Adding vertex %s" % vertex)
curve.add_point(vertex)
#print("Storing curve to extras under &'%s'" % extras.Path)
extras[extras.Path] = curve
else:
#print("!!!Node is path, but is not MeshInstance3D!!!")
push_error((
"Node marked to become a path " +
"is not a MeshInstance3D: %s"
) % what)
&'Spawner':
replacement = Spawner.new()
&'VisualLiquid':
#print("Instantiating VisualLiquid node")
replacement = load("res://zones/Liquid/VisualLiquid.gd").new()
#print(
#"Trying to load LiquidDescriptor from %s" %
#resource_path
#)
if ResourceLoader.exists(resource_path):
replacement.descriptor = load(resource_path)
#print(
#"Loaded LiquidDescriptor: %s" %
#replacement.descriptor
#)
else:
#print("!!!LiquidDescriptor does not exist!!!")
push_error(
"LiquidDescriptor @ %s not found" % resource_path
)
assert(what is MeshInstance3D)
#print("Grabbing mesh from original")
replacement.mesh = what.mesh
#print("Attaching liquid shader as override")
replacement.set_surface_override_material(0, preload(
"res://zones/Liquid/LiquidMaterial.tres"
))
_:
if ResourceLoader.exists(scene_path, &'PackedScene'):
#print("Instantiating %s node from scene %s" % [
#node_type, scene_path
#])
replacement = load(scene_path).instantiate()
#print("Result: %s" % replacement)
elif ResourceLoader.exists(script_path, &'GDScript'):
#print("Instantiating %s node from script %s" % [
#node_type, script_path
#])
replacement = load(script_path).new()
#print("Result: %s" % replacement)
instantiated_from_script = true
else:
#print("!!!Node type does not exist: %s %s!!!" % [
#node_type, extras[node_type]
#])
push_error("No such %s: %s (what: %s)" % [
node_type, extras[node_type], what
])
#print("Node-category-specific setup done")
if discard:
#print("Node will be discarded")
var parent := what.get_parent()
if not recursive_discard:
#print("Propagating children up to parent")
for child in what.get_children():
#print("Removing child %s" % child)
what.remove_child(child)
#print("Adding to parent")
what.get_parent().add_child(child)
#print("Removing discarded node from tree")
parent.remove_child(what)
#print("Queuing discarded node to be freed")
what.queue_free()
else:
if replacement:
#print("Adding instantiated node %s to tree" % replacement)
if (
node_type == &'ConditionalSpawnGroup' or
node_type == &'DeferredSpawnGroup' or
node_type == &'Spawner'
):
replacement.root = what
var parent := what.get_parent()
parent.remove_child(what)
parent.add_child(replacement)
elif node_type == &'Zone' and what is MeshInstance3D:
#print((
#"Node is a zone, and original was a MeshInstance3D " +
#"(%s); therefore, the instantiated zone belongs " +
#"under the original MeshInstance3D as a child, " +
#"rather than in its place as a replacement; " +
#"adding as child"
#) % what)
replacement.name = extras.Zone
what.add_child(replacement)
elif node_type == &'Liquid' and what is MeshInstance3D:
#print((
#"Node is a liquid, " +
#"and original was a MeshInstance3D " +
#"(%s); therefore, the instantiated liquid belongs " +
#"under the original MeshInstance3D as a child, " +
#"rather than in its place as a replacement; " +
#"adding as child"
#) % what)
replacement.name = &'Liquid'
what.add_child(replacement)
#print("Generating collision meshes")
for collider in PlaneConvexDecompose.from(
what.mesh, extras.get(&'Depth', DEFAULT_LIQUID_DEPTH)
):
replacement.add_child(collider)
collider.owner = root
#print("Generating collision meshes " +
#"from the original MeshInstance3D " +
#"for use for the instantiated liquid")
#var colliders := _get_multiple_convex_collisions(
#what, _convex_decomposition_settings
#)
#print("Adding collision meshes as children")
#for collider in colliders:
#print("Adding collision mesh %s" % collider)
#replacement.add_child(collider)
#print("Generating collision trimesh")
#var collider := _get_trimesh(what)
#replacement.add_child(collider)
elif (
node_type == &'Object' and instantiated_from_script and
(what is MeshInstance3D)
):
#print((
#"Node is a game-object instantiated from a script, " +
#"not a scene, and original was a MeshInstance3D " +
#"(%s); therefore, the original MeshInstance3D " +
#"should be preserved under its replacement " +
#"as a child, and also used to generate " +
#"collision meshes"
#) % what)
#print("Recording original's mesh and materials")
_record_mesh_and_mats(what, root, what.mesh, meshes, mats)
#print("Replacing original with replacement")
replacement.name = what.name
_replace_node_maybe_with_scene_instance(
what, replacement
)
#print("Adding original as child to replacement")
what.name = &'MeshInstance3D'
replacement.add_child(what)
#print("Generating collision meshes")
var colliders := _get_multiple_convex_collisions(
what, _convex_decomposition_settings
)
#print("Adding collision meshes as children")
for collider in colliders:
#print("Adding collision mesh %s" % collider)
replacement.add_child(collider)
else:
#print((
#"Replacing original node (%s) " +
#"with instantiated replacement (%s)"
#) % [what, replacement])
replacement.name = what.name
_replace_node_maybe_with_scene_instance(
what, replacement
)
#print("Queuing original to be freed")
what.queue_free()
if replacement is Node3D and what is Node3D and not (
what.get_parent() == replacement
) and not (
replacement.get_parent() == what
):
#print("Both instantiated node and original are Node3D; " +
#"ergo, copying transform")
replacement.transform = what.transform
elif replacement is Node2D and what is Node2D and not (
what.get_parent() == replacement
) and not (
replacement.get_parent() == what
):
#print("Both instantiated node and original are Node2D; " +
#"ergo, copying transform (%s)" % what.transform)
replacement.transform = what.transform
if node_type == &'Zone' or ((
node_type == &'Liquid' or
node_type == &'InvisibleWall'
) and not (what is MeshInstance3D)):
#print("Instantiated node still needs a collision mesh, " +
#"but building one from the original node " +
#"is either not appropriate (if instantiated node " +
#"is a zone) or not possible (if original node " +
#"is not a MeshInstance3D); will use a BoxShape3D")
#print("Instantiating CollisionShape3D")
var collision_shape := CollisionShape3D.new()
collision_shape.name = &'CollisionShape3D'
#print("Instantiating BoxShape3D resource")
collision_shape.shape = BoxShape3D.new()
#print("Setting box size from node scale (2.0*%s)" %
#what.scale)
collision_shape.shape.size = 2.0*what.scale
#print("Setting node scale to identity")
replacement.scale = Vector3.ONE
#print("Adding collision mesh as child")
replacement.add_child(collision_shape)
#print("Setting collision mesh's transform to identity")
collision_shape.transform = Transform3D.IDENTITY
#print("Copying extras to replacement")
replacement.set_meta(&'extras', extras)
what = replacement
if what is MeshInstance3D:
#print("Node is MeshInstance3D; recording mesh and materials")
_record_mesh_and_mats(what, root, what.mesh, meshes, mats)
if (
node_type != &'VisualLiquid' and
not extras.get(&'Intangible', false)
):
#print("Node is not intangible; creating collision mesh")
what.create_trimesh_collision()
#print("Recording node under its unique name")
nodes_by_unique_name[unique_name] = what
func _get_multiple_convex_collisions(
what: MeshInstance3D,
params: MeshConvexDecompositionSettings = null
) -> Array[CollisionShape3D]:
#print("Creating collision meshes from node %s")
var children_before := what.get_children()
what.create_multiple_convex_collisions(params)
var children_after := what.get_children()
#print("Searching children to find result of operation")
var staticbody: StaticBody3D
for child in children_after:
if not (child in children_before):
#print("Result found: %s" % child)
assert(child is StaticBody3D and not staticbody)
staticbody = child
#print("Removing resulting staticbody from tree")
what.remove_child(staticbody)
#print("Collecting collision meshes from staticbody")
var colliders: Array[CollisionShape3D] = []
for collider in staticbody.get_children():
assert(collider is CollisionShape3D)
#print("Removing collision mesh %s from staticbody" % collider)
staticbody.remove_child(collider)
#print("Queuing up collision mesh")
colliders.push_back(collider)
#print("Queuing staticbody to be freed")
staticbody.queue_free()
return colliders
func _get_trimesh(what: MeshInstance3D) -> CollisionShape3D:
#print("Creating collision mesh from node %s")
var children_before := what.get_children()
what.create_trimesh_collision()
var children_after := what.get_children()
#print("Searching children to find result of operation")
var staticbody: StaticBody3D
for child in children_after:
if not (child in children_before):
#print("Result found: %s" % child)
assert(child is StaticBody3D and not staticbody)
staticbody = child
#print("Removing resulting staticbody from tree")
what.remove_child(staticbody)
#print("Collecting collision meshes from staticbody")
var colliders: Array[CollisionShape3D] = []
for collider in staticbody.get_children():
assert(collider is CollisionShape3D)
#print("Removing collision mesh %s from staticbody" % collider)
staticbody.remove_child(collider)
#print("Queuing up collision mesh")
colliders.push_back(collider)
#print("Queuing staticbody to be freed")
staticbody.queue_free()
assert(colliders.size() == 1)
return colliders[0]
func _apply_shaders(meshes: Array[Mesh], mats: Array[Material]) -> void:
#print("Building array of ShaderMaterial replacements " +
#"for original materials")
var shadmats: Array[ShaderMaterial] = []
var shaders := {}
for mat in mats:
#print("Building ShaderMaterial to replace %s" % mat)
var extras := {}
if mat.has_meta(&'extras'):
extras = mat.get_meta(&'extras')
var shadmat := ShaderMaterial.new()
shadmats.push_back(shadmat)
if extras.has(&'Shader'):
#print("Material says to use shader &'%s'; " +
# "loading" % extras.Shader)
shadmat.shader = load("res://vfx/%s.gdshader" % extras.Shader)
else:
#print("Material does not specify a shader; will use &'basic'")
shadmat.shader = preload("res://vfx/basic.gdshader")
shaders[
StringName(extras.Shader) if extras.has(&'Shader') else &'basic'
] = true
assert(mat is BaseMaterial3D)
#print((
#"Setting shader parameter &'image' " +
#"to original material's texture (%s)"
#) % mat.albedo_texture)
shadmat.set_shader_parameter(&'image', mat.albedo_texture)
for k in extras:
if k == k.to_lower():
#print((
#"Setting shader parameter &'%s' " +
#"to value specified in original material's extras: %s"
#) % [k, extras[k]])
shadmat.set_shader_parameter(k, extras[k])
print("Loaded shaders: %s" % (", ".join(shaders.keys())))
#print("Replacing meshes' original materials with ShaderMaterials")
for mesh in meshes:
#print("Replacing materials in mesh %s" % mesh)
for i in mesh.get_surface_count():
#print("Replacing material for surface %d" % i)
var j: int = mats.find(mesh.surface_get_material(i))
if j >= 0:
mesh.surface_set_material(i, shadmats[j])
func _apply_properties(
what: Node, root: Node,
nodes_by_unique_name: Dictionary
) -> void:
for child in what.get_children():
_apply_properties(child, root, nodes_by_unique_name)
if (
(what is ConditionalSpawnGroup) or
(what is DeferredSpawnGroup) or
(what is Spawner)
):
_apply_properties(what.root, what.root, nodes_by_unique_name)
_apply_properties_to_node(what, root, nodes_by_unique_name)
func _apply_properties_to_node(
what: Node, root: Node,
nodes_by_unique_name: Dictionary
) -> void:
#print("Setting properties for node %s from its extras" % what)
var extras := what.get_meta(&'extras', {}) as Dictionary
for key in extras:
for descriptor in what.get_property_list():
if descriptor.name.capitalize() == key.capitalize():
#print("Interpreting value of extra &'%s'" % key)
var value: Variant = _interpret_property_value(
descriptor, extras[key], what, root,
nodes_by_unique_name
)
#print("Value interpreted as %s" % value)
print("Setting property: $'%s'.%s = %s" % [
root.get_path_to(what), key, value
])
what.set(key, value)
break
func _interpret_property_value(
descriptor: Dictionary,
value: Variant,
what: Node,
root: Node,
nodes_by_unique_name: Dictionary
) -> Variant:
#print("First, parsing property hint string '%s'" % descriptor.hint_string)
var hint_args: PackedStringArray = descriptor.hint_string.split(',')
var hint_arg_args: Array[PackedStringArray] = []
for i in hint_args.size():
var arg_args := hint_args[i].split(':')
hint_args[i] = arg_args[0]
arg_args.remove_at(0)
hint_arg_args.push_back(arg_args)
#print("Checking property type")
match descriptor.type:
TYPE_NIL:
#print("Nil property can only be null")
return null
TYPE_BOOL:
#print("Property is bool; double-negating extra")
return not not value
TYPE_INT: match descriptor.hint:
PROPERTY_HINT_ENUM, PROPERTY_HINT_ENUM_SUGGESTION:
#print("Property is enum")
if value is String:
#print("Extra is string; searching enum")
var i = hint_args.find(value.capitalize())
if i >= 0 and hint_arg_args[i].size() > 0:
#print(
#"Found enum value corresponding to given string"
#)
return hint_arg_args[i][0]
else:
#print("Could not find enum value " +
#"corresponding to given string; defaulting to -1")
return -1
else:
#print("Extra is not string; skipping enum search " +
#"and using directly")
return int(value)
_:
#print("Property is int")
return int(value)
TYPE_FLOAT:
#print("Property is float")
return float(value)
TYPE_STRING:
#print("Property is string")
return String(value)
TYPE_VECTOR2:
#print("Property is Vector2; ergo, " +
#"assuming extra is 2-element float array")
return Vector2(value[0], value[1])
TYPE_VECTOR2I:
#print("Property is Vector2i; ergo, " +
#"assuming extra is 2-element int array")
return Vector2i(value[0], value[1])
TYPE_RECT2:
#print("Property is Rect2; ergo, " +
#"assuming extra is 2-element float array")
return Rect2(value[0], value[1])
TYPE_RECT2I:
#print("Property is Rect2i; ergo, " +
#"assuming extra is 2-element int array")
return Rect2i(value[0], value[1])
TYPE_VECTOR3:
#print("Property is Vector3; ergo, " +
#"assuming extra is 3-element float array")
return Vector3(value[0], value[1], value[2])
TYPE_VECTOR3I:
#print("Property is Vector3i; ergo, " +
#"assuming extra is 3-element int array")
return Vector3i(value[0], value[1], value[2])
TYPE_TRANSFORM2D:
#print("Property is Transform2D; ergo, " +
#"assuming extra is 6-element float array")
return Transform2D(
value[0], Vector2(value[1], value[2]),
value[3], Vector2(value[4], value[5])
)
TYPE_VECTOR4:
#print("Property is Vector4; ergo, " +
#"assuming extra is 4-element float array")
return Vector4(value[0], value[1], value[2], value[3])
TYPE_VECTOR4I:
#print("Property is Vector4i; ergo, " +
#"assuming extra is 4-element int array")
return Vector4i(value[0], value[1], value[2], value[3])
TYPE_PLANE:
#print("Property is Plane; ergo, " +
#"assuming extra is float array with 3, 4, 6, or 9 elements")
match value.size():
3:
#print("3 elements; ergo, using Plane(Vector3)")
return Plane(Vector3(value[0], value[1], value[2]))
4:
#print("4 elements; ergo, using Plane(Vector3, float)")
return Plane(
Vector3(value[0], value[1], value[2]),
value[3]
)
6:
#print("6 elements; ergo, using Plane(Vector3, Vector3)")
return Plane(
Vector3(value[0], value[1], value[2]),
Vector3(value[3], value[4], value[5])
)
9:
#print("9 elements; ergo, " +
#"using Plane(Vector3, Vector3, Vector3)")
return Plane(
Vector3(value[0], value[1], value[2]),
Vector3(value[3], value[4], value[5]),
Vector3(value[6], value[7], value[8])
)
_:
#print((
#"!!!Wrong number of elements (%d); " +
#"ergo, using Plane()!!!"
#) % value.size())
push_error((
"Arguments %s are invalid " +
"for constructing Plane"
) % value)
return Plane()
TYPE_QUATERNION:
#print("Property is Quaternion; ergo, " +
#"assuming extra is 4-element float array")
return Quaternion(
value[0], value[1], value[2], value[3]
)
TYPE_AABB:
#print("Property is AABB; ergo, " +
#"assuming extra is 6-element float array")
return AABB(
Vector3(value[0], value[1], value[2]),
Vector3(value[3], value[4], value[5])
)
TYPE_BASIS:
#print("Property is Basis; ergo, " +
#"assuming extra is 9-element float array")
return Basis(
Vector3(value[0], value[1], value[2]),
Vector3(value[3], value[4], value[5]),
Vector3(value[6], value[7], value[8])
)
TYPE_TRANSFORM3D:
#print("Property is Transform3D; ergo, " +
#"assuming extra is 12-element float array")
return Transform3D(
Basis(
Vector3(value[0], value[1], value[2]),
Vector3(value[3], value[4], value[5]),
Vector3(value[6], value[7], value[8])
),
Vector3(value[9], value[10], value[11])
)
TYPE_PROJECTION:
#print("Property is Projection; ergo, " +
#"assuming extra is float array with 12 or 16 elements")
match value.size():
12:
#print("12 elements; ergo, " +
#"using Projection(Transform3D)")
return Projection(Transform3D(
Basis(
Vector3(value[0], value[1], value[2]),
Vector3(value[3], value[4], value[5]),
Vector3(value[6], value[7], value[8])
),
Vector3(value[9], value[10], value[11])
))
16:
#print("16 elements; ergo, " +
#"using Projection(" +
#"Vector4, Vector4, Vector4, Vector4" +
#")")
return Projection(
Vector4(value[0], value[1], value[2], value[3]),
Vector4(value[4], value[5], value[6], value[7]),
Vector4(value[8], value[9], value[10], value[11]),
Vector4(value[12], value[13], value[14], value[15])
)
_:
#print((
#"!!!Wrong number of elements (%d); " +
#"ergo, using Projection()!!!"
#) % value.size())
push_error((
"Arguments %s are invalid " +
"for constructing Projection"
) % value)
return Projection()
TYPE_COLOR:
#print("Property is Color")
if value is String:
#print("Extra is String; parsing as Color")
return Color(value)
else:
#print("Extra is not String; ergo, " +
#"assuming float array with 3 or 4 elements")
match value.size():
3:
#print("3 elements; ergo, RGB")
return Color(value[0], value[1], value[2])
4:
#print("4 elements; ergo, RGBA")
return Color(value[0], value[1], value[2], value[3])
_:
#print((
#"!!!Wrong number of elements (%d); " +
#"ergo, using Color.WHITE!!!"
#) % value.size())
push_error((
"Arguments %s are invalid " +
"for constructing Color"
) % value)
return Color.WHITE
TYPE_STRING_NAME:
#print("Property is StringName; interning extra")
return StringName(String(value))
TYPE_NODE_PATH:
#print("Property is NodePath; parsing extra")
return NodePath(String(value))
TYPE_RID:
#print("Property is RID; " +
#"interpreting extra as resource path (without res://)")
return load("res://" + String(value)).get_rid()
TYPE_OBJECT: match descriptor.hint:
PROPERTY_HINT_RESOURCE_TYPE:
#print("Property is Resource")
# special case for assigning resources from discarded nodes
if value is Resource:
#print("Extra is Resource, likely built " +
#"from discarded node during node setup phase; " +
#"using unmodified")
return value
else:
#print("Interpreting extra as resource path " +
#"(without res://)")
return load("res://" + String(value))
PROPERTY_HINT_NODE_TYPE:
#print("Property is Node")
if nodes_by_unique_name.has(value):
#print("Extra refers to a node's unique name " +
#"as defined in the glTF data; " +
#"using that node")
return nodes_by_unique_name.get(value)
elif String(value).begins_with('/'):
#print("Extra begins with /; " +
#"interpreting as absolute node path")
return root.get_node(value)
else:
#print("Interpreting extra as node path")
return what.get_node(value)
_:
#print("Property is Object; interpreting extra " +
#"as name usable with ClassDB")
return ClassDB.instantiate(StringName(String(value)))
TYPE_CALLABLE:
#print("Property is callable; " +
#"interpreting extra as method reference (:-style)")
#print("Parsing method reference for receiver and method name")
var parts := String(value).split(':')
#print("Parse result: %s" % parts)
if parts.size() == 1:
#print("No receiver; assuming implicit self")
return Callable(what, parts[0])
elif nodes_by_unique_name.has(parts[0]):
#print("Receiver refers to a node's unique name " +
#"as defined in the glTF data; " +
#"using that node")
return Callable(nodes_by_unique_name.get(parts[0]), parts[1])
elif String(parts[0]).begins_with('/'):
#print("Receiver begins with /; " +
#"interpreting as absolute node path")
return Callable(root.get_node(parts[0]), parts[1])
else:
#print("Interpreting receiver as node path")
return Callable(what.get_node(parts[0]), parts[1])
TYPE_SIGNAL:
#print("Property is signal; " +
#"interpreting extra as signal reference (:-style)")
#print("Parsing signal reference for emitter and signal name")
var parts := String(value).split(':')
#print("Parse result: %s" % parts)
if parts.size() == 1:
#print("No emitter; assuming implicit self")
return what.get(parts[0])
elif nodes_by_unique_name.has(parts[0]):
#print("Emitter refers to a node's unique name " +
#"as defined in the glTF data; " +
#"using that node")
return nodes_by_unique_name.get(parts[0]).get(parts[1])
elif String(parts[0]).begins_with('/'):
#print("Emitter begins with /; " +
#"interpreting as absolute node path")
return root.get_node(parts[0]).get(parts[1])
else:
#print("Interpreting emitter as node path")
return what.get_node(parts[0]).get(parts[1])
TYPE_DICTIONARY:
#print("Property is Dictionary; interpreting extra as JSON")
return JSON.parse_string(value)
TYPE_ARRAY:
#print("Property is array; assuming extra is array")
return value
TYPE_PACKED_BYTE_ARRAY:
#print("Property is PackedByteArray; assuming extra is int array")
return PackedByteArray(value)
TYPE_PACKED_INT32_ARRAY:
#print("Property is PackedInt32Array; assuming extra is int array")
return PackedInt32Array(value)
TYPE_PACKED_INT64_ARRAY:
#print("Property is PackedInt64Array; assuming extra is int array")
return PackedInt64Array(value)
TYPE_PACKED_FLOAT32_ARRAY:
#print("Property is PackedFloat32Array; " +
#"assuming extra is float array")
return PackedFloat32Array(value)
TYPE_PACKED_FLOAT64_ARRAY:
#print("Property is PackedFloat64Array; " +
#"assuming extra is float array")
return PackedFloat64Array(value)
TYPE_PACKED_STRING_ARRAY:
#print("Property is PackedStringArray; " +
#"treating extra as String containing comma-separated tokens")
return PackedStringArray(String(value).split(','))
TYPE_PACKED_VECTOR2_ARRAY:
#print("Property is PackedVector2Array; ergo, " +
#"assuming extra is float array of size divisible by 2")
var vecs: Array[Vector2] = []
var current := Vector2.ZERO
var current_i: int = 0
for num in value:
current[current_i] = value
current_i += 1
if current_i >= 2:
current_i = 0
#print("Assembled %s" % current)
vecs.push_back(current)
return PackedVector2Array(vecs)
TYPE_PACKED_VECTOR3_ARRAY:
#print("Property is PackedVector3Array; ergo, " +
#"assuming extra is float array of size divisible by 3")
var vecs: Array[Vector2] = []
var current := Vector3.ZERO
var current_i: int = 0
for num in value:
current[current_i] = value
current_i += 1
if current_i >= 3:
current_i = 0
#print("Assembled %s" % current)
vecs.push_back(current)
return PackedVector3Array(vecs)
TYPE_PACKED_COLOR_ARRAY:
#print("Property is PackedColorArray")
var colors: Array[Color] = []
if value is String:
#print("Extra is string; parsing as comma-separated tokens")
for token in value.split(','):
#print("Parsing token '%s' as a Color" % token)
colors.push_back(Color(token))
else:
#print("Assuming extra is float array of size divisible by 4")
var current := Color.WHITE
var current_i: int = 0
for num in value:
current[current_i] = value
current_i += 1
if current_i >= 4:
current_i = 0
#print("Assembled %s" % current)
colors.push_back(current)
return PackedColorArray(colors)
_:
#print("Property is of unknown or unspecified type; " +
#"returning extra unmodified")
return value
func _replace_node_maybe_with_scene_instance(x: Node, y: Node) -> void:
var scene_path := y.scene_file_path
x.replace_by(y, true)
y.scene_file_path = scene_path