gdharness 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +163 -85
- package/build/godot/addons/gdharness_editor/animation_reads.gd +14 -11
- package/build/godot/addons/gdharness_editor/declared_values.gd +157 -4
- package/build/godot/addons/gdharness_editor/file_reads.gd +15 -3
- package/build/godot/addons/gdharness_editor/file_walk.gd +134 -0
- package/build/godot/addons/gdharness_editor/file_walk.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/headless_check.gd +44 -23
- package/build/godot/addons/gdharness_editor/property_paths.gd +225 -0
- package/build/godot/addons/gdharness_editor/property_paths.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +249 -227
- package/build/godot/addons/gdharness_editor/reading.gd +15 -0
- package/build/godot/addons/gdharness_editor/same_values.gd +177 -0
- package/build/godot/addons/gdharness_editor/same_values.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +100 -15
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -3
- package/build/godot/addons/gdharness_editor/tool_executor.gd +12 -3
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +402 -55
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +3 -2
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +91 -0
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +88 -5
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +102 -337
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +363 -0
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +15 -0
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +10 -26
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +3 -2
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +2 -1
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +30 -0
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -3
- package/build/godot/operations/animation_reads.gd +14 -11
- package/build/godot/operations/declared_values.gd +157 -4
- package/build/godot/operations/dependencies.gd +14 -39
- package/build/godot/operations/editor_tools.gd +7 -3
- package/build/godot/operations/file_reads.gd +15 -3
- package/build/godot/operations/file_walk.gd +1 -0
- package/build/godot/operations/gdscript_analysis.gd +229 -75
- package/build/godot/operations/gdscript_authoring.gd +142 -34
- package/build/godot/operations/import_pipeline.gd +17 -6
- package/build/godot/operations/logger.gd +7 -0
- package/build/godot/operations/property_paths.gd +225 -0
- package/build/godot/operations/property_values.gd +249 -227
- package/build/godot/operations/reading.gd +15 -0
- package/build/godot/operations/same_values.gd +177 -0
- package/build/godot/operations/serialisation.gd +75 -3
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
extends RefCounted
|
|
3
|
+
|
|
4
|
+
## The groups a node of a scene is in. A scene's own file puts a node in a group and can take it
|
|
5
|
+
## out of one it put it in; a group the scene that makes the node puts it in, the one it is
|
|
6
|
+
## instanced from or one this scene inherits, can be changed only there, as the editor's group dock
|
|
7
|
+
## shows it locked.
|
|
8
|
+
|
|
9
|
+
const GdhSceneFile = preload("../scene_file.gd")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## The groups of [param node], sorted, as {groups, groupsHere}: groupsHere, those this scene's file
|
|
13
|
+
## puts it in, is given only where another scene puts it in some of them. Empty for a node in none.
|
|
14
|
+
static func read(scene: GdhSceneFile, node: Node) -> Dictionary:
|
|
15
|
+
var all: Array = _sorted(node.get_groups())
|
|
16
|
+
if all.is_empty():
|
|
17
|
+
return {}
|
|
18
|
+
var answer: Dictionary = {"groups": all}
|
|
19
|
+
var here: Array = _sorted(scene.groups_at(scene.path_of(node)))
|
|
20
|
+
if here.size() != all.size():
|
|
21
|
+
answer["groupsHere"] = here
|
|
22
|
+
return answer
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## The group names under [param key] of [param args], which the server has checked are strings, or
|
|
26
|
+
## none when not given.
|
|
27
|
+
static func asked(args: Dictionary, key: String) -> Array:
|
|
28
|
+
var given: Variant = args.get(key)
|
|
29
|
+
return given if given is Array else []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Puts [param node] in each group of [param adding] and takes it out of each of [param removing],
|
|
33
|
+
## or answers why not with nothing changed.
|
|
34
|
+
static func change(scene: GdhSceneFile, node: Node, adding: Array, removing: Array) -> String:
|
|
35
|
+
for group: Variant in adding + removing:
|
|
36
|
+
if str(group).strip_edges().is_empty():
|
|
37
|
+
return (
|
|
38
|
+
"%s names no group: a group's name has something in it besides spaces."
|
|
39
|
+
% JSON.stringify(group)
|
|
40
|
+
)
|
|
41
|
+
for group: Variant in adding:
|
|
42
|
+
if removing.has(group):
|
|
43
|
+
return "%s is in both addGroups and removeGroups." % str(group)
|
|
44
|
+
var at: String = scene.path_of(node)
|
|
45
|
+
var here: PackedStringArray = scene.groups_at(at)
|
|
46
|
+
for group: Variant in removing:
|
|
47
|
+
var name: String = str(group)
|
|
48
|
+
if not node.is_in_group(name):
|
|
49
|
+
var held: Array = _sorted(node.get_groups())
|
|
50
|
+
return (
|
|
51
|
+
"%s is in no group %s; %s."
|
|
52
|
+
% [node.name, name, "it is in " + ", ".join(held) if not held.is_empty() else "it is in none"]
|
|
53
|
+
)
|
|
54
|
+
if not here.has(name):
|
|
55
|
+
var from: String = scene.values_from(node, at)
|
|
56
|
+
if from.is_empty():
|
|
57
|
+
return (
|
|
58
|
+
"%s is in %s, which no scene file puts it in: something puts it there as it is made, such as its script."
|
|
59
|
+
% [node.name, name]
|
|
60
|
+
)
|
|
61
|
+
return (
|
|
62
|
+
(
|
|
63
|
+
"%s is in %s because %s puts it there, and no scene using that one can take it out: "
|
|
64
|
+
+ "remove the group in %s, which takes it out in every scene using that one."
|
|
65
|
+
)
|
|
66
|
+
% [node.name, name, from, from]
|
|
67
|
+
)
|
|
68
|
+
for group: Variant in adding:
|
|
69
|
+
node.add_to_group(str(group), true)
|
|
70
|
+
for group: Variant in removing:
|
|
71
|
+
node.remove_from_group(str(group))
|
|
72
|
+
return ""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Why [param saved], the node as the written file loads it, is not in the groups asked, or "".
|
|
76
|
+
static func unkept(saved: Node, adding: Array, removing: Array) -> String:
|
|
77
|
+
for group: Variant in adding:
|
|
78
|
+
if not saved.is_in_group(str(group)):
|
|
79
|
+
return "%s would load in no group %s." % [saved.name, str(group)]
|
|
80
|
+
for group: Variant in removing:
|
|
81
|
+
if saved.is_in_group(str(group)):
|
|
82
|
+
return "%s would still load in the group %s." % [saved.name, str(group)]
|
|
83
|
+
return ""
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
static func _sorted(groups: Variant) -> Array:
|
|
87
|
+
var names: Array = []
|
|
88
|
+
for group: Variant in groups:
|
|
89
|
+
names.append(str(group))
|
|
90
|
+
names.sort()
|
|
91
|
+
return names
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://76w2hadtl7nab
|
|
@@ -4,8 +4,9 @@ extends RefCounted
|
|
|
4
4
|
## What a new node in a scene is made or copied from, what the scene owns of it, and where among its
|
|
5
5
|
## siblings a node may go.
|
|
6
6
|
|
|
7
|
+
const GdhFileWalk = preload("../file_walk.gd")
|
|
7
8
|
const GdhRead = preload("../reading.gd")
|
|
8
|
-
const
|
|
9
|
+
const GdhSame = preload("../same_values.gd")
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
## The scene to instance for an add, or null for a node of a class, beside why neither can be made
|
|
@@ -17,11 +18,15 @@ static func what_to_add(node_type: String, instance_path: String, scene_path: St
|
|
|
17
18
|
"add takes nodeType for a node of a class, or instancePath for a scene, and one of them only."
|
|
18
19
|
]
|
|
19
20
|
if not instance_path.is_empty():
|
|
21
|
+
# Asked before the cycle walk, which reads the file's dependencies and so loaded a file that
|
|
22
|
+
# was not there, laying the engine's error and a backtrace beneath the refusal.
|
|
23
|
+
if not ResourceLoader.exists(instance_path, "PackedScene"):
|
|
24
|
+
var said: String = "No scene at %s to instance" % instance_path
|
|
25
|
+
var offered: String = GdhFileWalk.new().with_nearest_files(said, instance_path)
|
|
26
|
+
return [null, offered if offered != said else said + "."]
|
|
20
27
|
var cycle: String = _instancing_cycle(scene_path, instance_path)
|
|
21
28
|
if not cycle.is_empty():
|
|
22
29
|
return [null, cycle]
|
|
23
|
-
if not ResourceLoader.exists(instance_path, "PackedScene"):
|
|
24
|
-
return [null, "No scene at %s to instance." % instance_path]
|
|
25
30
|
var packed: PackedScene = ResourceLoader.load(instance_path, "PackedScene") as PackedScene
|
|
26
31
|
if packed == null or not packed.can_instantiate():
|
|
27
32
|
return [null, "%s does not load as a scene that can be instanced." % instance_path]
|
|
@@ -35,12 +40,24 @@ static func what_to_add(node_type: String, instance_path: String, scene_path: St
|
|
|
35
40
|
)
|
|
36
41
|
]
|
|
37
42
|
if not ClassDB.class_exists(node_type) or not ClassDB.is_parent_class(node_type, "Node"):
|
|
38
|
-
return [null, "nodeType
|
|
43
|
+
return [null, not_a_node_class("nodeType", node_type)]
|
|
39
44
|
if not ClassDB.can_instantiate(node_type):
|
|
40
45
|
return [null, "%s cannot be instantiated." % node_type]
|
|
41
46
|
return [null, ""]
|
|
42
47
|
|
|
43
48
|
|
|
49
|
+
## The refusal for [param given], sent as [param argument], which names no node class, with the
|
|
50
|
+
## node classes nearest it, so "Node2DD" is offered Node2D.
|
|
51
|
+
static func not_a_node_class(argument: String, given: String) -> String:
|
|
52
|
+
var said: String = "%s must be a node class, and %s is not one" % [argument, given]
|
|
53
|
+
var classes: PackedStringArray = ClassDB.get_inheriters_from_class("Node")
|
|
54
|
+
var _added: bool = classes.append("Node")
|
|
55
|
+
var close: PackedStringArray = GdhRead.nearest(given, classes)
|
|
56
|
+
if close.is_empty():
|
|
57
|
+
return said + "."
|
|
58
|
+
return "%s; the nearest %s %s." % [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
59
|
+
|
|
60
|
+
|
|
44
61
|
## A new node of [param node_type], or an instance of [param packed] when there is one. The instance
|
|
45
62
|
## is made in the editor's edit state, so the pack writes it as an instance of its file, and owning
|
|
46
63
|
## its root leaves its own nodes to the instanced scene.
|
|
@@ -68,7 +85,7 @@ static func copied(source: Node, root: Node, copies: Dictionary) -> Node:
|
|
|
68
85
|
continue
|
|
69
86
|
var property: String = str(declared.get("name", ""))
|
|
70
87
|
var value: Variant = source.get(property)
|
|
71
|
-
if not
|
|
88
|
+
if not GdhSame.same(copy.get(property), value):
|
|
72
89
|
copy.set(property, value)
|
|
73
90
|
for group: StringName in source.get_groups():
|
|
74
91
|
if not copy.is_in_group(group):
|
|
@@ -154,6 +171,72 @@ static func place(node: Node, asked: Variant) -> void:
|
|
|
154
171
|
node.get_parent().move_child(node, GdhRead.as_int(asked))
|
|
155
172
|
|
|
156
173
|
|
|
174
|
+
## The properties that hold where [param node] is drawn, which a reparent keeping it in place
|
|
175
|
+
## changes: a Control keeps only its position, as Node.reparent keeps it.
|
|
176
|
+
static func placing(node: Node) -> Array[String]:
|
|
177
|
+
if node is Node3D:
|
|
178
|
+
return ["position", "rotation", "scale"]
|
|
179
|
+
if node is Control:
|
|
180
|
+
return ["position"]
|
|
181
|
+
if node is Node2D:
|
|
182
|
+
return ["position", "rotation", "scale", "skew"]
|
|
183
|
+
return []
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
## Gives [param node], just moved from under [param was], the transform that keeps it where it was
|
|
187
|
+
## drawn, as the editor's reparent and Node.reparent do. Worked out from the local transforms
|
|
188
|
+
## rather than read off the global ones, which the engine answers only for a node in the tree, and
|
|
189
|
+
## the scene a tool edits is loaded from its file and never is. Nothing changes for a top-level
|
|
190
|
+
## node, or where the old and new parents place their children alike.
|
|
191
|
+
static func keep_in_place(node: Node, was: Node) -> void:
|
|
192
|
+
if node is Node3D:
|
|
193
|
+
var spatial: Node3D = node
|
|
194
|
+
var before: Transform3D = _spatial_space(was)
|
|
195
|
+
var after: Transform3D = _spatial_space(node.get_parent())
|
|
196
|
+
if not spatial.top_level and not before.is_equal_approx(after):
|
|
197
|
+
spatial.transform = after.affine_inverse() * before * spatial.transform
|
|
198
|
+
elif node is CanvasItem:
|
|
199
|
+
var drawn: CanvasItem = node
|
|
200
|
+
var before_2d: Transform2D = _canvas_space(was)
|
|
201
|
+
var after_2d: Transform2D = _canvas_space(node.get_parent())
|
|
202
|
+
if drawn.top_level or before_2d.is_equal_approx(after_2d):
|
|
203
|
+
return
|
|
204
|
+
var held: Transform2D = after_2d.affine_inverse() * before_2d * drawn.get_transform()
|
|
205
|
+
if node is Control:
|
|
206
|
+
var control: Control = node
|
|
207
|
+
control.position = held.origin
|
|
208
|
+
else:
|
|
209
|
+
var flat: Node2D = node
|
|
210
|
+
flat.transform = held
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
## Where the children of [param parent] are drawn in 3D: the transforms of it and the Node3Ds
|
|
214
|
+
## above it, up to the first that is top-level or whose parent is no Node3D.
|
|
215
|
+
static func _spatial_space(parent: Node) -> Transform3D:
|
|
216
|
+
var space: Transform3D = Transform3D.IDENTITY
|
|
217
|
+
var at: Node = parent
|
|
218
|
+
while at is Node3D:
|
|
219
|
+
var spatial: Node3D = at
|
|
220
|
+
space = spatial.transform * space
|
|
221
|
+
if spatial.top_level:
|
|
222
|
+
break
|
|
223
|
+
at = at.get_parent()
|
|
224
|
+
return space
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
## Where the children of [param parent] are drawn in 2D, as [method _spatial_space] works it out in 3D.
|
|
228
|
+
static func _canvas_space(parent: Node) -> Transform2D:
|
|
229
|
+
var space: Transform2D = Transform2D.IDENTITY
|
|
230
|
+
var at: Node = parent
|
|
231
|
+
while at is CanvasItem:
|
|
232
|
+
var drawn: CanvasItem = at
|
|
233
|
+
space = drawn.get_transform() * space
|
|
234
|
+
if drawn.top_level:
|
|
235
|
+
break
|
|
236
|
+
at = at.get_parent()
|
|
237
|
+
return space
|
|
238
|
+
|
|
239
|
+
|
|
157
240
|
## Why [param instanced] cannot be instanced into [param scene_path], or "" when it can: a scene
|
|
158
241
|
## inside itself, directly or through the scenes it instances, which Godot's own dialog refuses and
|
|
159
242
|
## which never finishes loading.
|