gdharness 1.3.42 → 1.3.44
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 +383 -243
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +55 -33
- package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
- package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_editor/file_reads.gd +83 -42
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +68 -44
- package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
- package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
- package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
- package/build/godot/addons/gdharness_editor/reading.gd +51 -17
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +127 -80
- package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +37 -27
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +79 -49
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +65 -42
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +166 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +68 -41
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +12 -5
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +113 -67
- package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
- package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
- package/build/godot/addons/gdharness_runtime/reading.gd +51 -17
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +149 -147
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +34 -17
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd +15 -15
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_tests/bench.gd +4 -5
- package/build/godot/addons/gdharness_tests/event.gd +10 -5
- package/build/godot/addons/gdharness_tests/share.gd +38 -18
- package/build/godot/addons/gdharness_tests/suites.gd +3 -3
- package/build/godot/addons/gdharness_tests/wait.gd +3 -2
- package/build/godot/loader/loader.gd +9 -9
- package/build/godot/operations/animation_reads.gd +55 -33
- package/build/godot/operations/audio_buses.gd +48 -19
- package/build/godot/operations/class_cache.gd +16 -11
- package/build/godot/operations/classdb_queries.gd +89 -44
- package/build/godot/operations/declared_values.gd +68 -32
- package/build/godot/operations/dependencies.gd +190 -109
- package/build/godot/operations/editor_tools.gd +10 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +83 -42
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +86 -65
- package/build/godot/operations/gdscript_authoring.gd +83 -36
- package/build/godot/operations/godot_operations.gd +17 -11
- package/build/godot/operations/import_pipeline.gd +110 -59
- package/build/godot/operations/input_actions.gd +62 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +61 -32
- package/build/godot/operations/project_diagnostics.gd +24 -11
- package/build/godot/operations/project_file.gd +16 -16
- package/build/godot/operations/project_validation.gd +71 -44
- package/build/godot/operations/property_paths.gd +22 -12
- package/build/godot/operations/property_values.gd +143 -78
- package/build/godot/operations/reading.gd +51 -17
- package/build/godot/operations/rename_checks.gd +2 -2
- package/build/godot/operations/resource_files.gd +11 -9
- package/build/godot/operations/same_values.gd +4 -4
- package/build/godot/operations/scene_connections.gd +27 -13
- package/build/godot/operations/script_check.gd +52 -49
- package/build/godot/operations/serialisation.gd +75 -51
- package/build/godot/operations/setting_reads.gd +7 -6
- package/build/godot/operations/value_shapes.gd +5 -6
- package/build/godot/tests/listed.gd +321 -268
- package/build/keeper.js +43 -43
- package/package.json +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
|
-
##
|
|
5
|
-
## or by instancing it with its children editable.
|
|
3
|
+
## The two ways a scene keeps its own values for nodes another scene makes: by inheriting that
|
|
4
|
+
## scene, or by instancing it with its children editable.
|
|
6
5
|
|
|
7
6
|
const GdhFileReads = preload("../file_reads.gd")
|
|
8
7
|
const GdhPaths = preload("scene_paths.gd")
|
|
@@ -18,11 +17,11 @@ static func taken(scene_path: String) -> Dictionary:
|
|
|
18
17
|
"error":
|
|
19
18
|
(
|
|
20
19
|
(
|
|
21
|
-
"%s already exists, and scene_create op=create would replace everything in it.
|
|
22
|
-
+ "with scene_node, or create the scene under another path."
|
|
20
|
+
"%s already exists, and scene_create op=create would replace everything in it. "
|
|
21
|
+
+ "Edit it with scene_node, or create the scene under another path."
|
|
23
22
|
)
|
|
24
23
|
% scene_path
|
|
25
|
-
)
|
|
24
|
+
),
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
|
|
@@ -32,12 +31,15 @@ static func inherited(
|
|
|
32
31
|
args: Dictionary, scene_path: String, base_path: String, script_path: String
|
|
33
32
|
) -> Dictionary:
|
|
34
33
|
if scene_path.is_empty():
|
|
35
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
34
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
36
35
|
if args.has("rootNodeType"):
|
|
37
36
|
return {
|
|
38
37
|
"ok": false,
|
|
39
38
|
"error":
|
|
40
|
-
|
|
39
|
+
(
|
|
40
|
+
"inherits takes the root's class from %s, so rootNodeType is not given with it."
|
|
41
|
+
% base_path
|
|
42
|
+
),
|
|
41
43
|
}
|
|
42
44
|
if FileAccess.file_exists(scene_path):
|
|
43
45
|
return taken(scene_path)
|
|
@@ -45,10 +47,10 @@ static func inherited(
|
|
|
45
47
|
if not script_path.is_empty():
|
|
46
48
|
script = load(script_path) as Script
|
|
47
49
|
if script == null:
|
|
48
|
-
return {"ok": false, "error": "No script loads from " + script_path}
|
|
50
|
+
return { "ok": false, "error": "No script loads from " + script_path }
|
|
49
51
|
var uncompiled: String = GdhSceneFile.uncompiled(script, script_path)
|
|
50
52
|
if not uncompiled.is_empty():
|
|
51
|
-
return {"ok": false, "error": uncompiled}
|
|
53
|
+
return { "ok": false, "error": uncompiled }
|
|
52
54
|
return inherit(scene_path, base_path, str(args.get("rootNodeName", "")), script)
|
|
53
55
|
|
|
54
56
|
|
|
@@ -57,17 +59,22 @@ static func inherited(
|
|
|
57
59
|
## given. Answers what scene_create answers, or a refusal.
|
|
58
60
|
##
|
|
59
61
|
## Written as the text the editor's New Inherited Scene saves rather than packed from an instance of
|
|
60
|
-
## the base: a headless engine making that instance runs every script in the base, and the pack
|
|
61
|
-
## keep what they set as this scene's own values.
|
|
62
|
-
static func inherit(
|
|
62
|
+
## the base: a headless engine making that instance runs every script in the base, and the pack
|
|
63
|
+
## would keep what they set as this scene's own values.
|
|
64
|
+
static func inherit(
|
|
65
|
+
scene_path: String, base_path: String, root_name: String, script: Script
|
|
66
|
+
) -> Dictionary:
|
|
63
67
|
var base: Dictionary = GdhFileReads.scene_node(base_path, ".")
|
|
64
68
|
if not base.get("ok", false):
|
|
65
|
-
return {
|
|
69
|
+
return {
|
|
70
|
+
"ok": false,
|
|
71
|
+
"error": "inherits: " + str(base.get("error", "%s holds no nodes" % base_path)),
|
|
72
|
+
}
|
|
66
73
|
var root_type: String = str(base["type"])
|
|
67
74
|
var name: String = str(base["name"]) if root_name.is_empty() else root_name
|
|
68
75
|
var unfit: String = GdhSceneFile.unfit_name(name)
|
|
69
76
|
if not unfit.is_empty():
|
|
70
|
-
return {"ok": false, "error": unfit}
|
|
77
|
+
return { "ok": false, "error": unfit }
|
|
71
78
|
if script != null:
|
|
72
79
|
var extended: String = str(script.get_instance_base_type())
|
|
73
80
|
if not ClassDB.is_parent_class(root_type, extended):
|
|
@@ -77,11 +84,11 @@ static func inherit(scene_path: String, base_path: String, root_name: String, sc
|
|
|
77
84
|
(
|
|
78
85
|
"%s extends %s, so it cannot be on the %s root %s inherits."
|
|
79
86
|
% [script.resource_path, extended, root_type, base_path]
|
|
80
|
-
)
|
|
87
|
+
),
|
|
81
88
|
}
|
|
82
89
|
var folder: String = GdhSceneFile.make_folder_for(scene_path)
|
|
83
90
|
if not folder.is_empty():
|
|
84
|
-
return {"ok": false, "error": folder}
|
|
91
|
+
return { "ok": false, "error": folder }
|
|
85
92
|
|
|
86
93
|
var lines: PackedStringArray = [
|
|
87
94
|
'[gd_scene format=3 uid="%s"]' % ResourceUID.id_to_text(ResourceUID.create_id()),
|
|
@@ -105,18 +112,24 @@ static func inherit(scene_path: String, base_path: String, root_name: String, sc
|
|
|
105
112
|
if file == null or not file.store_string("\n".join(lines) + "\n"):
|
|
106
113
|
return {
|
|
107
114
|
"ok": false,
|
|
108
|
-
"error":
|
|
115
|
+
"error":
|
|
116
|
+
"Could not write %s: %s" % [scene_path, error_string(FileAccess.get_open_error())],
|
|
109
117
|
}
|
|
110
118
|
file.close()
|
|
111
119
|
|
|
112
|
-
var loaded: PackedScene = ResourceLoader.load(
|
|
120
|
+
var loaded: PackedScene = ResourceLoader.load(
|
|
121
|
+
scene_path, "PackedScene", ResourceLoader.CACHE_MODE_IGNORE
|
|
122
|
+
)
|
|
113
123
|
var instanced: PackedScene = loaded.get_state().get_node_instance(0) if loaded != null else null
|
|
114
124
|
if instanced == null or instanced.resource_path != base_path:
|
|
115
125
|
var _removed: Error = DirAccess.remove_absolute(scene_path)
|
|
116
126
|
return {
|
|
117
127
|
"ok": false,
|
|
118
128
|
"error":
|
|
119
|
-
|
|
129
|
+
(
|
|
130
|
+
"%s did not load back as a scene inheriting %s, so it was removed."
|
|
131
|
+
% [scene_path, base_path]
|
|
132
|
+
),
|
|
120
133
|
}
|
|
121
134
|
GdhSceneFile.refresh(scene_path)
|
|
122
135
|
var answer: Dictionary = {
|
|
@@ -124,7 +137,7 @@ static func inherit(scene_path: String, base_path: String, root_name: String, sc
|
|
|
124
137
|
"scenePath": scene_path,
|
|
125
138
|
"inherits": base_path,
|
|
126
139
|
"rootNodeType": root_type,
|
|
127
|
-
"rootNodeName": name
|
|
140
|
+
"rootNodeName": name,
|
|
128
141
|
}
|
|
129
142
|
var properties: Dictionary = base["properties"]
|
|
130
143
|
var carried: Variant = script if script != null else properties.get("script")
|
|
@@ -139,7 +152,8 @@ static func inherit(scene_path: String, base_path: String, root_name: String, sc
|
|
|
139
152
|
## Makes the children of [param node], an instance in [param scene], editable or not, answering why
|
|
140
153
|
## it cannot or "". With them editable the scene keeps its own values for them, as the editor's
|
|
141
154
|
## Editable Children does; made not editable again, the values it kept for them are dropped. The
|
|
142
|
-
## caller has had [method GdhSceneFile.foreign] refuse a node inside an instance that is not
|
|
155
|
+
## caller has had [method GdhSceneFile.foreign] refuse a node inside an instance that is not
|
|
156
|
+
## editable.
|
|
143
157
|
static func set_editable(scene: GdhSceneFile, node: Node, editable: bool) -> String:
|
|
144
158
|
if node.owner == null or node.scene_file_path.is_empty():
|
|
145
159
|
return (
|
|
@@ -161,7 +175,9 @@ static func set_editable(scene: GdhSceneFile, node: Node, editable: bool) -> Str
|
|
|
161
175
|
## for a node deleted, they are only named. Answers followed, the scenes rewritten, and
|
|
162
176
|
## notFollowed, each one left as it was with why, each only when it names any, with what
|
|
163
177
|
## [param carried] gives.
|
|
164
|
-
static func follow(
|
|
178
|
+
static func follow(
|
|
179
|
+
changed: String, old_path: String, new_path: String, carried: GdhPaths
|
|
180
|
+
) -> Dictionary:
|
|
165
181
|
var texts: Dictionary = {}
|
|
166
182
|
_read_scenes("res://", texts)
|
|
167
183
|
var kin: Array[String] = [changed]
|
|
@@ -187,7 +203,7 @@ static func follow(changed: String, old_path: String, new_path: String, carried:
|
|
|
187
203
|
moves[old_path] = new_path
|
|
188
204
|
var read: Dictionary = GdhFileReads.scene_tree(scene_path, -1, false, false)
|
|
189
205
|
if not read.get("ok", false):
|
|
190
|
-
not_followed.append({"scenePath": scene_path, "reason": str(read.get("error", ""))})
|
|
206
|
+
not_followed.append({ "scenePath": scene_path, "reason": str(read.get("error", "")) })
|
|
191
207
|
continue
|
|
192
208
|
var tree: Dictionary = read["tree"]
|
|
193
209
|
_instances_of(tree, changed, old_path, new_path, moves)
|
|
@@ -198,28 +214,29 @@ static func follow(changed: String, old_path: String, new_path: String, carried:
|
|
|
198
214
|
rewritten = _rewritten(text, moves, old_path.get_base_dir() != new_path.get_base_dir())
|
|
199
215
|
if rewritten != text:
|
|
200
216
|
if not unsaved.is_empty():
|
|
201
|
-
not_followed.append({"scenePath": scene_path, "reason": unsaved})
|
|
217
|
+
not_followed.append({ "scenePath": scene_path, "reason": unsaved })
|
|
202
218
|
continue
|
|
203
219
|
var file: FileAccess = FileAccess.open(scene_path, FileAccess.WRITE)
|
|
204
220
|
if file == null or not file.store_string(rewritten):
|
|
205
221
|
not_followed.append(
|
|
206
|
-
{"scenePath": scene_path, "reason": error_string(FileAccess.get_open_error())}
|
|
222
|
+
{ "scenePath": scene_path, "reason": error_string(FileAccess.get_open_error()) }
|
|
207
223
|
)
|
|
208
224
|
continue
|
|
209
225
|
file.close()
|
|
210
226
|
GdhSceneFile.refresh(scene_path)
|
|
211
227
|
followed.append(scene_path)
|
|
212
228
|
var holds_paths: bool = text.contains("NodePath(")
|
|
213
|
-
# A script is only read, for the lines naming the node, so it does not need the file
|
|
229
|
+
# A script is only read, for the lines naming the node, so it does not need the file
|
|
230
|
+
# written.
|
|
214
231
|
if moves.is_empty() or not (holds_paths or text.contains('Script"')):
|
|
215
232
|
continue
|
|
216
233
|
if holds_paths and not unsaved.is_empty() and not new_path.is_empty():
|
|
217
|
-
not_followed.append({"scenePath": scene_path, "reason": unsaved})
|
|
234
|
+
not_followed.append({ "scenePath": scene_path, "reason": unsaved })
|
|
218
235
|
continue
|
|
219
236
|
var before: int = carried.retargeted.size()
|
|
220
237
|
var unwritten: String = carried.carry_into(scene_path, moves)
|
|
221
238
|
if not unwritten.is_empty() and not followed.has(scene_path):
|
|
222
|
-
not_followed.append({"scenePath": scene_path, "reason": unwritten})
|
|
239
|
+
not_followed.append({ "scenePath": scene_path, "reason": unwritten })
|
|
223
240
|
elif carried.retargeted.size() > before and not followed.has(scene_path):
|
|
224
241
|
followed.append(scene_path)
|
|
225
242
|
var answer: Dictionary = carried.answer()
|
|
@@ -238,7 +255,9 @@ static func _read_scenes(directory: String, texts: Dictionary) -> void:
|
|
|
238
255
|
_read_scenes(directory.path_join(name), texts)
|
|
239
256
|
for name: String in DirAccess.get_files_at(directory):
|
|
240
257
|
if name.ends_with(".tscn"):
|
|
241
|
-
texts[directory.path_join(name)] = FileAccess.get_file_as_string(
|
|
258
|
+
texts[directory.path_join(name)] = FileAccess.get_file_as_string(
|
|
259
|
+
directory.path_join(name)
|
|
260
|
+
)
|
|
242
261
|
|
|
243
262
|
|
|
244
263
|
## Whether the scene at [param scene_path] inherits [param changed], through any number of scenes.
|
|
@@ -253,19 +272,22 @@ static func _inherits(scene_path: String, changed: String, texts: Dictionary) ->
|
|
|
253
272
|
return false
|
|
254
273
|
|
|
255
274
|
|
|
256
|
-
## The scene the root of [param text] inherits, or "": a root entry naming an instance and no
|
|
275
|
+
## The scene the root of [param text] inherits, or "": a root entry naming an instance and no
|
|
276
|
+
## parent.
|
|
257
277
|
static func _base_of(text: String) -> String:
|
|
258
278
|
for line: String in text.split("\n"):
|
|
259
279
|
if not line.begins_with("[node "):
|
|
260
280
|
continue
|
|
261
|
-
var instanced: RegExMatch =
|
|
262
|
-
line
|
|
281
|
+
var instanced: RegExMatch = (
|
|
282
|
+
RegEx.create_from_string('instance=ExtResource\\("([^"]+)"\\)').search(line)
|
|
263
283
|
)
|
|
264
284
|
if instanced == null or line.contains(" parent="):
|
|
265
285
|
return ""
|
|
266
286
|
var named: RegExMatch = (
|
|
267
287
|
RegEx
|
|
268
|
-
. create_from_string(
|
|
288
|
+
. create_from_string(
|
|
289
|
+
'\\[ext_resource [^\\n]*path="([^"]+)" id="%s"\\]' % instanced.get_string(1)
|
|
290
|
+
)
|
|
269
291
|
. search(text)
|
|
270
292
|
)
|
|
271
293
|
return named.get_string(1) if named != null else ""
|
|
@@ -289,10 +311,11 @@ static func _instances_of(
|
|
|
289
311
|
|
|
290
312
|
## [param text] with every path [param moves] names, and every path below one, moved.
|
|
291
313
|
##
|
|
292
|
-
## Beside a path the saver writes the unique_ids of the nodes along it, which the loader follows
|
|
293
|
-
## the path has vanished. A rename leaves those ids as they were; with [param reparented] they
|
|
294
|
-
## through the old parent, which still exists, so a recovery would find the wrong node. They are
|
|
295
|
-
## out of each path moved, as a hand-written scene leaves them, and the next save writes them
|
|
314
|
+
## Beside a path the saver writes the unique_ids of the nodes along it, which the loader follows
|
|
315
|
+
## when the path has vanished. A rename leaves those ids as they were; with [param reparented] they
|
|
316
|
+
## run through the old parent, which still exists, so a recovery would find the wrong node. They are
|
|
317
|
+
## left out of each path moved, as a hand-written scene leaves them, and the next save writes them
|
|
318
|
+
## anew.
|
|
296
319
|
static func _rewritten(text: String, moves: Dictionary, reparented: bool) -> String:
|
|
297
320
|
if moves.is_empty():
|
|
298
321
|
return text
|
|
@@ -314,7 +337,11 @@ static func _rewritten(text: String, moves: Dictionary, reparented: bool) -> Str
|
|
|
314
337
|
var placed: String = _attribute_set(
|
|
315
338
|
line, "parent", "." if moved_parent.is_empty() else moved_parent
|
|
316
339
|
)
|
|
317
|
-
line =
|
|
340
|
+
line = (
|
|
341
|
+
_without_ids(placed, "parent_id_path")
|
|
342
|
+
if placed != line and reparented
|
|
343
|
+
else placed
|
|
344
|
+
)
|
|
318
345
|
else:
|
|
319
346
|
line = _attribute_moved(line, "parent", "parent_id_path", moves, reparented)
|
|
320
347
|
lines[at] = line
|
|
@@ -338,8 +365,8 @@ static func _attribute_set(line: String, attribute: String, value: String) -> St
|
|
|
338
365
|
return line.substr(0, found.get_start(1)) + value + line.substr(found.get_end(1))
|
|
339
366
|
|
|
340
367
|
|
|
341
|
-
## [param line] with the path under [param attribute] moved by [param moves], and, when that moved
|
|
342
|
-
## and the move was a reparent, without the ids under [param ids] that went along the old path.
|
|
368
|
+
## [param line] with the path under [param attribute] moved by [param moves], and, when that moved
|
|
369
|
+
## it and the move was a reparent, without the ids under [param ids] that went along the old path.
|
|
343
370
|
static func _attribute_moved(
|
|
344
371
|
line: String, attribute: String, ids: String, moves: Dictionary, reparented: bool
|
|
345
372
|
) -> String:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## The node paths scenes hold, carried after a node moves as the editor's own rename carries them:
|
|
5
4
|
## the NodePath values on their nodes, inside arrays, dictionaries and the resources built into the
|
|
6
5
|
## scene too, and the tracks of the animations their players play. After a delete they are named
|
|
@@ -9,7 +8,11 @@ extends RefCounted
|
|
|
9
8
|
## A path is worked out from where its holder was and where it pointed before the move, so it is
|
|
10
9
|
## followed in a scene loaded after the move as well, where the old path no longer resolves.
|
|
11
10
|
|
|
12
|
-
enum {
|
|
11
|
+
enum {
|
|
12
|
+
UNCHANGED,
|
|
13
|
+
REWRITTEN,
|
|
14
|
+
DELETED,
|
|
15
|
+
}
|
|
13
16
|
|
|
14
17
|
const GdhHeadlessCheck = preload("../headless_check.gd")
|
|
15
18
|
const GdhRead = preload("../reading.gd")
|
|
@@ -87,7 +90,7 @@ func carry(scene: GdhSceneFile, moves: Dictionary) -> int:
|
|
|
87
90
|
var name: String = str(property["name"])
|
|
88
91
|
if not _may_hold_paths(property):
|
|
89
92
|
continue
|
|
90
|
-
var entry: Dictionary = {"scenePath": scene.path, "nodePath": now, "property": name}
|
|
93
|
+
var entry: Dictionary = { "scenePath": scene.path, "nodePath": now, "property": name }
|
|
91
94
|
var elsewhere: String = "" if set_here.has(name) else from
|
|
92
95
|
var carried: Array = _carried(node.get(name), was, now, entry, elsewhere)
|
|
93
96
|
if carried[0]:
|
|
@@ -241,10 +244,12 @@ static func _may_hold_paths(property: Dictionary) -> bool:
|
|
|
241
244
|
## it carried, as [whether any was, the value]. A resource built into the scene is changed in place.
|
|
242
245
|
## [param entry] says where the value is held, for the record of each path. A path in a resource may
|
|
243
246
|
## be relative to something other than its holder, as the editor allows; it is followed only when it
|
|
244
|
-
## names a node from where its holder was, as one relative to the holder does. A value
|
|
245
|
-
## elsewhere], a file other than the scene's, gives is named only when it names nothing since
|
|
246
|
-
## move, which its own file's pass could not see.
|
|
247
|
-
func _carried(
|
|
247
|
+
## names a node from where its holder was, as one relative to the holder does. A value
|
|
248
|
+
## [param elsewhere], a file other than the scene's, gives is named only when it names nothing since
|
|
249
|
+
## the move, which its own file's pass could not see.
|
|
250
|
+
func _carried(
|
|
251
|
+
value: Variant, was: String, now: String, entry: Dictionary, elsewhere: String
|
|
252
|
+
) -> Array:
|
|
248
253
|
match typeof(value):
|
|
249
254
|
TYPE_NODE_PATH:
|
|
250
255
|
var path: NodePath = value
|
|
@@ -256,7 +261,10 @@ func _carried(value: Variant, was: String, now: String, entry: Dictionary, elsew
|
|
|
256
261
|
not_retargeted.append(
|
|
257
262
|
_left(
|
|
258
263
|
record,
|
|
259
|
-
|
|
264
|
+
(
|
|
265
|
+
"The value is set in %s, and an edit here would not be saved there."
|
|
266
|
+
% elsewhere
|
|
267
|
+
)
|
|
260
268
|
)
|
|
261
269
|
)
|
|
262
270
|
elif after[0] == DELETED:
|
|
@@ -350,7 +358,9 @@ func _after(path: NodePath, was: String, now: String) -> Array:
|
|
|
350
358
|
## Whether [param path], held by the node at [param now], names no node.
|
|
351
359
|
func _names_nothing(path: NodePath, now: String) -> bool:
|
|
352
360
|
var holder: Node = _scene.root.get_node_or_null(now)
|
|
353
|
-
return
|
|
361
|
+
return (
|
|
362
|
+
holder != null and holder.get_node_or_null(NodePath(path.get_concatenated_names())) == null
|
|
363
|
+
)
|
|
354
364
|
|
|
355
365
|
|
|
356
366
|
## The path to [param target] through the unique node that was at [param start], written through its
|
|
@@ -424,8 +434,9 @@ func _tracks(mixer: AnimationMixer, now: String) -> void:
|
|
|
424
434
|
var animation: Animation = library.get_animation(animation_name)
|
|
425
435
|
var home: Dictionary = _home_of(animation, library)
|
|
426
436
|
var why: String = home["why"]
|
|
427
|
-
# Carried once, from the first scene playing it. One kept in a file of its own that this
|
|
428
|
-
# rewrote is held in _homes, so a later scene loading that file is given this same
|
|
437
|
+
# Carried once, from the first scene playing it. One kept in a file of its own that this
|
|
438
|
+
# call rewrote is held in _homes, so a later scene loading that file is given this same
|
|
439
|
+
# copy.
|
|
429
440
|
if _seen.has(animation):
|
|
430
441
|
continue
|
|
431
442
|
_seen[animation] = true
|
|
@@ -466,13 +477,14 @@ func _tracks(mixer: AnimationMixer, now: String) -> void:
|
|
|
466
477
|
|
|
467
478
|
|
|
468
479
|
## Where an edit to [param animation], played from [param library], is saved, as {file, home, why,
|
|
469
|
-
## theirs}: file "" with the scene, or the file of its own it or its library is kept in, with home
|
|
470
|
-
## resource saved there; why, when nothing written here would keep the edit; theirs, when that
|
|
471
|
-
## because it is kept in another scene or an imported file, whose tracks are written in its
|
|
480
|
+
## theirs}: file "" with the scene, or the file of its own it or its library is kept in, with home
|
|
481
|
+
## the resource saved there; why, when nothing written here would keep the edit; theirs, when that
|
|
482
|
+
## is because it is kept in another scene or an imported file, whose tracks are written in its
|
|
483
|
+
## terms.
|
|
472
484
|
func _home_of(animation: Animation, library: AnimationLibrary) -> Dictionary:
|
|
473
485
|
var file: String = animation.resource_path.get_slice("::", 0)
|
|
474
486
|
if file.is_empty() or file == _scene.path:
|
|
475
|
-
return {"file": "", "why": ""}
|
|
487
|
+
return { "file": "", "why": "" }
|
|
476
488
|
var home: Resource = animation
|
|
477
489
|
if animation.resource_path.contains("::"):
|
|
478
490
|
home = library if library.resource_path == file else null
|
|
@@ -483,18 +495,21 @@ func _home_of(animation: Animation, library: AnimationLibrary) -> Dictionary:
|
|
|
483
495
|
}
|
|
484
496
|
if not Engine.is_editor_hint():
|
|
485
497
|
var script: Script = home.get_script()
|
|
486
|
-
if (
|
|
498
|
+
if (
|
|
499
|
+
(script != null and not script.is_tool())
|
|
500
|
+
or GdhHeadlessCheck.holds_scripted_embedded(home, {})
|
|
501
|
+
):
|
|
487
502
|
return {
|
|
488
503
|
"why":
|
|
489
504
|
(
|
|
490
505
|
(
|
|
491
506
|
(
|
|
492
|
-
"%s holds a resource with a script that is not a tool, which a
|
|
493
|
-
+ "it loads the file. "
|
|
507
|
+
"%s holds a resource with a script that is not a tool, which a "
|
|
508
|
+
+ "headless engine runs as it loads the file. "
|
|
494
509
|
)
|
|
495
510
|
% file
|
|
496
511
|
)
|
|
497
512
|
+ GdhHeadlessCheck.REMEDY
|
|
498
|
-
)
|
|
513
|
+
),
|
|
499
514
|
}
|
|
500
|
-
return {"file": file, "home": home, "why": ""}
|
|
515
|
+
return { "file": file, "home": home, "why": "" }
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## What a new node in a scene is made or copied from, what the scene owns of it, and where among its
|
|
5
4
|
## siblings a node may go.
|
|
6
5
|
|
|
@@ -15,7 +14,8 @@ static func what_to_add(node_type: String, instance_path: String, scene_path: St
|
|
|
15
14
|
if node_type.is_empty() == instance_path.is_empty():
|
|
16
15
|
return [
|
|
17
16
|
null,
|
|
18
|
-
"add takes nodeType for a node of a class, or instancePath for a scene, and one of
|
|
17
|
+
"add takes nodeType for a node of a class, or instancePath for a scene, and one of "
|
|
18
|
+
+ "them only.",
|
|
19
19
|
]
|
|
20
20
|
if not instance_path.is_empty():
|
|
21
21
|
# Asked before the cycle walk, which reads the file's dependencies and so loaded a file that
|
|
@@ -35,9 +35,12 @@ static func what_to_add(node_type: String, instance_path: String, scene_path: St
|
|
|
35
35
|
return [
|
|
36
36
|
null,
|
|
37
37
|
(
|
|
38
|
-
|
|
38
|
+
(
|
|
39
|
+
"nodeType must be a node class, and %s is a scene: pass it as instancePath to "
|
|
40
|
+
+ "instance it."
|
|
41
|
+
)
|
|
39
42
|
% node_type
|
|
40
|
-
)
|
|
43
|
+
),
|
|
41
44
|
]
|
|
42
45
|
if not ClassDB.class_exists(node_type) or not ClassDB.is_parent_class(node_type, "Node"):
|
|
43
46
|
return [null, not_a_node_class("nodeType", node_type)]
|
|
@@ -73,10 +76,12 @@ static func made(packed: PackedScene, node_type: String) -> Node:
|
|
|
73
76
|
static func copied(source: Node, root: Node, copies: Dictionary) -> Node:
|
|
74
77
|
var copy: Node = null
|
|
75
78
|
if not source.scene_file_path.is_empty():
|
|
76
|
-
# An instanced scene made again from its file, so the copy is an instance too.
|
|
77
|
-
# makes a plain node of the instance's class, and the pack saved it with
|
|
78
|
-
# instance's root frozen into this scene.
|
|
79
|
-
var packed: PackedScene =
|
|
79
|
+
# An instanced scene made again from its file, so the copy is an instance too.
|
|
80
|
+
# Node.duplicate makes a plain node of the instance's class, and the pack saved it with
|
|
81
|
+
# every property of the instance's root frozen into this scene.
|
|
82
|
+
var packed: PackedScene = (
|
|
83
|
+
ResourceLoader.load(source.scene_file_path, "PackedScene") as PackedScene
|
|
84
|
+
)
|
|
80
85
|
if packed == null:
|
|
81
86
|
return null
|
|
82
87
|
copy = packed.instantiate(PackedScene.GEN_EDIT_STATE_INSTANCE)
|
|
@@ -123,7 +128,11 @@ static func reconnect(copies: Dictionary, top: Node) -> void:
|
|
|
123
128
|
if not flags & Object.CONNECT_PERSIST or target == null:
|
|
124
129
|
continue
|
|
125
130
|
var to: Object = copies.get(target, target)
|
|
126
|
-
if
|
|
131
|
+
if (
|
|
132
|
+
to == target
|
|
133
|
+
and target is Node
|
|
134
|
+
and (target == top or top.is_ancestor_of(target as Node))
|
|
135
|
+
):
|
|
127
136
|
continue
|
|
128
137
|
var aimed: Callable = Callable(to, callable.get_method())
|
|
129
138
|
if (
|
|
@@ -131,7 +140,10 @@ static func reconnect(copies: Dictionary, top: Node) -> void:
|
|
|
131
140
|
and copy.connect(signal_name, aimed, flags) != OK
|
|
132
141
|
):
|
|
133
142
|
push_error(
|
|
134
|
-
|
|
143
|
+
(
|
|
144
|
+
"gdharness: could not copy the connection %s of %s"
|
|
145
|
+
% [signal_name, source.name]
|
|
146
|
+
)
|
|
135
147
|
)
|
|
136
148
|
|
|
137
149
|
|
|
@@ -150,7 +162,9 @@ static func own(node: Node, root: Node) -> void:
|
|
|
150
162
|
static func unplaceable(parent: Node, node: Node, asked: Variant) -> String:
|
|
151
163
|
if asked == null:
|
|
152
164
|
return ""
|
|
153
|
-
var others: int =
|
|
165
|
+
var others: int = (
|
|
166
|
+
parent.get_child_count() - (1 if node != null and node.get_parent() == parent else 0)
|
|
167
|
+
)
|
|
154
168
|
var at: int = GdhRead.as_int(asked, -1)
|
|
155
169
|
if at < 0 or at > others:
|
|
156
170
|
return (
|
|
@@ -159,7 +173,7 @@ static func unplaceable(parent: Node, node: Node, asked: Variant) -> String:
|
|
|
159
173
|
str(at) if asked is int or asked is float else JSON.stringify(asked),
|
|
160
174
|
others,
|
|
161
175
|
parent.name,
|
|
162
|
-
others
|
|
176
|
+
others,
|
|
163
177
|
]
|
|
164
178
|
)
|
|
165
179
|
return ""
|
|
@@ -234,7 +248,8 @@ static func _spatial_space(parent: Node) -> Transform3D:
|
|
|
234
248
|
return space
|
|
235
249
|
|
|
236
250
|
|
|
237
|
-
## Where the children of [param parent] are drawn in 2D, as [method _spatial_space] works it out in
|
|
251
|
+
## Where the children of [param parent] are drawn in 2D, as [method _spatial_space] works it out in
|
|
252
|
+
## 3D.
|
|
238
253
|
static func _canvas_space(parent: Node) -> Transform2D:
|
|
239
254
|
var space: Transform2D = Transform2D.IDENTITY
|
|
240
255
|
var at: Node = parent
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## Where else in a scene a resource built into it is held, since such a resource is one object
|
|
5
4
|
## wherever the scene uses it.
|
|
6
5
|
|
|
@@ -25,17 +24,22 @@ static func holders(scene: GdhSceneFile, node: Node, key: String) -> Array[Dicti
|
|
|
25
24
|
for at: Node in nodes:
|
|
26
25
|
for declared: Dictionary in at.get_property_list():
|
|
27
26
|
var property: String = str(declared.get("name", ""))
|
|
28
|
-
if
|
|
27
|
+
if (
|
|
28
|
+
property.is_empty()
|
|
29
|
+
or not (GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE)
|
|
30
|
+
):
|
|
29
31
|
continue
|
|
30
32
|
for found: String in _paths_to(at.get(property), property, wanted, {}):
|
|
31
33
|
if at != node or not key.begins_with(found + "/"):
|
|
32
|
-
found_at.append({"node": scene.path_of(at), "property": found})
|
|
34
|
+
found_at.append({ "node": scene.path_of(at), "property": found })
|
|
33
35
|
return found_at
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
## The paths below [param path] at which [param value] holds a resource of [param wanted], by
|
|
37
39
|
## instance id, stepping into lists and into resources built into a scene.
|
|
38
|
-
static func _paths_to(
|
|
40
|
+
static func _paths_to(
|
|
41
|
+
value: Variant, path: String, wanted: Dictionary, seen: Dictionary
|
|
42
|
+
) -> Array[String]:
|
|
39
43
|
var paths: Array[String] = []
|
|
40
44
|
if value is Array:
|
|
41
45
|
var items: Array = value
|
|
@@ -54,6 +58,9 @@ static func _paths_to(value: Variant, path: String, wanted: Dictionary, seen: Di
|
|
|
54
58
|
seen[id] = true
|
|
55
59
|
for declared: Dictionary in held.get_property_list():
|
|
56
60
|
var property: String = str(declared.get("name", ""))
|
|
57
|
-
if
|
|
61
|
+
if (
|
|
62
|
+
not property.is_empty()
|
|
63
|
+
and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE
|
|
64
|
+
):
|
|
58
65
|
paths.append_array(_paths_to(held.get(property), path + "/" + property, wanted, seen))
|
|
59
66
|
return paths
|