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,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## Scenes and their nodes, edited in the open editor and read back from what it holds.
|
|
5
4
|
|
|
6
5
|
const GdhDeclared = preload("../declared_values.gd")
|
|
@@ -46,8 +45,8 @@ func _kept(scene: GdhSceneFile, saved: Node, node: Node, names: Array) -> String
|
|
|
46
45
|
var loaded: Variant = _properties.read_at(copy, property)
|
|
47
46
|
var given: Variant = _properties.read_at(node, property)
|
|
48
47
|
if not scene.kept(saved, loaded, given):
|
|
49
|
-
# The file holds it, and the copy's own script set it again as the copy loaded, as it
|
|
50
|
-
# wherever the scene loads; an editor saves the same.
|
|
48
|
+
# The file holds it, and the copy's own script set it again as the copy loaded, as it
|
|
49
|
+
# will wherever the scene loads; an editor saves the same.
|
|
51
50
|
if scene.stores(at, property, given):
|
|
52
51
|
var on: Dictionary = scene.loads_as.get_or_add(at, {})
|
|
53
52
|
if GdhHeadlessCheck.untooled(copy.get_script()):
|
|
@@ -56,8 +55,8 @@ func _kept(scene: GdhSceneFile, saved: Node, node: Node, names: Array) -> String
|
|
|
56
55
|
continue
|
|
57
56
|
return (
|
|
58
57
|
(
|
|
59
|
-
"%s.%s would load from the file as %s: the scene does not keep it, because the
|
|
60
|
-
+ "is not one a scene stores or the node belongs to another scene."
|
|
58
|
+
"%s.%s would load from the file as %s: the scene does not keep it, because the "
|
|
59
|
+
+ "property is not one a scene stores or the node belongs to another scene."
|
|
61
60
|
)
|
|
62
61
|
% [at, property, JSON.stringify(_values.serialize_value(loaded), "", false)]
|
|
63
62
|
)
|
|
@@ -151,7 +150,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
151
150
|
var script_path: String = str(args.get("script", ""))
|
|
152
151
|
|
|
153
152
|
if scene_path.is_empty():
|
|
154
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
153
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
155
154
|
var made: Dictionary = GdhSceneFile.made_as(
|
|
156
155
|
root_node_type,
|
|
157
156
|
"" if script_path.is_empty() else GdhSceneFile.res_path(project_path, script_path),
|
|
@@ -159,18 +158,21 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
159
158
|
"rootNodeType"
|
|
160
159
|
)
|
|
161
160
|
if made.has("error"):
|
|
162
|
-
return {"ok": false, "error": made["error"]}
|
|
161
|
+
return { "ok": false, "error": made["error"] }
|
|
163
162
|
root_node_type = str(made["type"])
|
|
164
163
|
script_path = str(made["script"])
|
|
165
164
|
# A class that is not a node went into a Node variable, which is a runtime error the caller
|
|
166
165
|
# heard about only as "Invalid tool result".
|
|
167
|
-
if
|
|
168
|
-
|
|
166
|
+
if (
|
|
167
|
+
not ClassDB.class_exists(root_node_type)
|
|
168
|
+
or not ClassDB.is_parent_class(root_node_type, "Node")
|
|
169
|
+
):
|
|
170
|
+
return { "ok": false, "error": GdhPlacing.not_a_node_class("rootNodeType", root_node_type) }
|
|
169
171
|
if not ClassDB.can_instantiate(root_node_type):
|
|
170
|
-
return {"ok": false, "error": "%s cannot be instantiated." % root_node_type}
|
|
172
|
+
return { "ok": false, "error": "%s cannot be instantiated." % root_node_type }
|
|
171
173
|
var unfit: String = GdhSceneFile.unfit_name(root_node_name)
|
|
172
174
|
if not unfit.is_empty():
|
|
173
|
-
return {"ok": false, "error": unfit}
|
|
175
|
+
return { "ok": false, "error": unfit }
|
|
174
176
|
if FileAccess.file_exists(scene_path):
|
|
175
177
|
return GdhKin.taken(scene_path)
|
|
176
178
|
|
|
@@ -186,29 +188,32 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
186
188
|
var script: Script = load(full_script_path) as Script
|
|
187
189
|
if script == null:
|
|
188
190
|
root.free()
|
|
189
|
-
return {"ok": false, "error": "No script loads from " + full_script_path}
|
|
191
|
+
return { "ok": false, "error": "No script loads from " + full_script_path }
|
|
190
192
|
var uncompiled: String = GdhSceneFile.uncompiled(script, full_script_path)
|
|
191
193
|
if not uncompiled.is_empty():
|
|
192
194
|
root.free()
|
|
193
|
-
return {"ok": false, "error": uncompiled}
|
|
195
|
+
return { "ok": false, "error": uncompiled }
|
|
194
196
|
var base: String = str(script.get_instance_base_type())
|
|
195
197
|
if not ClassDB.is_parent_class(root_node_type, base):
|
|
196
198
|
root.free()
|
|
197
199
|
return {
|
|
198
200
|
"ok": false,
|
|
199
201
|
"error":
|
|
200
|
-
|
|
202
|
+
(
|
|
203
|
+
"%s extends %s, so it cannot be on a %s root."
|
|
204
|
+
% [full_script_path, base, root_node_type]
|
|
205
|
+
),
|
|
201
206
|
}
|
|
202
207
|
root.set_script(script)
|
|
203
|
-
# Named again, since a headless engine runs the script's _init as it is attached, and a
|
|
204
|
-
# name is no property the write compares.
|
|
208
|
+
# Named again, since a headless engine runs the script's _init as it is attached, and a
|
|
209
|
+
# node's name is no property the write compares.
|
|
205
210
|
root.name = root_node_name
|
|
206
211
|
|
|
207
212
|
var had_folder: bool = DirAccess.dir_exists_absolute(scene_path.get_base_dir())
|
|
208
213
|
var folder: String = GdhSceneFile.make_folder_for(scene_path)
|
|
209
214
|
if not folder.is_empty():
|
|
210
215
|
root.free()
|
|
211
|
-
return {"ok": false, "error": folder}
|
|
216
|
+
return { "ok": false, "error": folder }
|
|
212
217
|
# Read before the write, which frees the root.
|
|
213
218
|
var root_class: Dictionary = GdhSceneFile.class_of(root)
|
|
214
219
|
var written: Dictionary = scene.write()
|
|
@@ -219,7 +224,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
219
224
|
"ok": true,
|
|
220
225
|
"scenePath": scene_path,
|
|
221
226
|
"rootNodeType": root_class["nodeType"],
|
|
222
|
-
"rootNodeName": root_node_name
|
|
227
|
+
"rootNodeName": root_node_name,
|
|
223
228
|
}
|
|
224
229
|
if root_class.has("scriptClass"):
|
|
225
230
|
answer["scriptClass"] = root_class["scriptClass"]
|
|
@@ -239,7 +244,9 @@ func list_scene_nodes(args: Dictionary) -> Dictionary:
|
|
|
239
244
|
|
|
240
245
|
# Read as a headless engine reads it with no editor connected, so the answer does not depend on
|
|
241
246
|
# whether one is.
|
|
242
|
-
var read: Dictionary = GdhFileReads.scene_tree(
|
|
247
|
+
var read: Dictionary = GdhFileReads.scene_tree(
|
|
248
|
+
scene_path, depth, include_properties, include_defaults
|
|
249
|
+
)
|
|
243
250
|
if read.get("ok", false):
|
|
244
251
|
read["unsavedInEditor"] = GdhSceneFile.unsaved(scene_path)
|
|
245
252
|
return read
|
|
@@ -256,12 +263,12 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
256
263
|
var adding: Array = GdhGroups.asked(args, "addGroups")
|
|
257
264
|
|
|
258
265
|
if node_name.is_empty():
|
|
259
|
-
return {"ok": false, "error": "Missing nodeName"}
|
|
266
|
+
return { "ok": false, "error": "Missing nodeName" }
|
|
260
267
|
var class_script: Script = null
|
|
261
268
|
if not node_type.is_empty() and not ClassDB.class_exists(node_type):
|
|
262
269
|
var declared: Dictionary = GdhSceneFile.script_class(node_type, "Node")
|
|
263
270
|
if declared.has("error"):
|
|
264
|
-
return {"ok": false, "error": declared["error"]}
|
|
271
|
+
return { "ok": false, "error": declared["error"] }
|
|
265
272
|
var held: Variant = declared.get("script")
|
|
266
273
|
if held is Script:
|
|
267
274
|
class_script = held
|
|
@@ -269,7 +276,7 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
269
276
|
var making: Array = GdhPlacing.what_to_add(node_type, instance_path, scene_path)
|
|
270
277
|
var packed: PackedScene = making[0]
|
|
271
278
|
if not str(making[1]).is_empty():
|
|
272
|
-
return {"ok": false, "error": making[1]}
|
|
279
|
+
return { "ok": false, "error": making[1] }
|
|
273
280
|
|
|
274
281
|
var opened: Array = _open(scene_path, true)
|
|
275
282
|
var scene: GdhSceneFile = opened[0]
|
|
@@ -280,7 +287,9 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
280
287
|
var root: Node = scene.root
|
|
281
288
|
var parent: Node = GdhSceneFile.node_at(root, parent_node_path)
|
|
282
289
|
if not parent:
|
|
283
|
-
return scene.refuse(
|
|
290
|
+
return scene.refuse(
|
|
291
|
+
scene.not_found("Parent node not found: " + parent_node_path, parent_node_path)
|
|
292
|
+
)
|
|
284
293
|
var foreign: String = scene.foreign(parent)
|
|
285
294
|
if not foreign.is_empty():
|
|
286
295
|
return scene.refuse(foreign)
|
|
@@ -316,9 +325,9 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
316
325
|
|
|
317
326
|
var new_path: String = scene.path_of(new_node)
|
|
318
327
|
# Read before the write, which frees the scene it was added to.
|
|
319
|
-
var answer: Dictionary = {"ok": true, "nodeName": node_name}
|
|
328
|
+
var answer: Dictionary = { "ok": true, "nodeName": node_name }
|
|
320
329
|
answer.merge(GdhSceneFile.class_of(new_node))
|
|
321
|
-
answer.merge({"nodePath": new_path, "index": new_node.get_index()})
|
|
330
|
+
answer.merge({ "nodePath": new_path, "index": new_node.get_index() })
|
|
322
331
|
if not properties.is_empty():
|
|
323
332
|
var held: Dictionary = {}
|
|
324
333
|
for key: Variant in properties:
|
|
@@ -331,7 +340,10 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
331
340
|
var written: Dictionary = scene.write(
|
|
332
341
|
func(saved: Node) -> String:
|
|
333
342
|
var unkept: String = _kept(
|
|
334
|
-
scene,
|
|
343
|
+
scene,
|
|
344
|
+
saved,
|
|
345
|
+
new_node,
|
|
346
|
+
properties.keys() + (["script"] if class_script != null else [])
|
|
335
347
|
)
|
|
336
348
|
if unkept.is_empty() and saved.get_node_or_null(new_path) != null:
|
|
337
349
|
unkept = GdhGroups.unkept(saved.get_node(new_path), adding, [])
|
|
@@ -357,9 +369,9 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
357
369
|
"ok": false,
|
|
358
370
|
"error":
|
|
359
371
|
(
|
|
360
|
-
"The root cannot be deleted, since a scene has exactly one. Delete the nodes under
|
|
361
|
-
+ "make a scene with another root with scene_create."
|
|
362
|
-
)
|
|
372
|
+
"The root cannot be deleted, since a scene has exactly one. Delete the nodes under "
|
|
373
|
+
+ "it, or make a scene with another root with scene_create."
|
|
374
|
+
),
|
|
363
375
|
}
|
|
364
376
|
|
|
365
377
|
var opened: Array = _open(scene_path, true)
|
|
@@ -383,7 +395,7 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
383
395
|
node.get_parent().remove_child(node)
|
|
384
396
|
node.free()
|
|
385
397
|
var carried: GdhPaths = GdhPaths.new()
|
|
386
|
-
var _rewritten: int = carried.carry(scene, {at: ""})
|
|
398
|
+
var _rewritten: int = carried.carry(scene, { at: "" })
|
|
387
399
|
|
|
388
400
|
var written: Dictionary = scene.write(
|
|
389
401
|
func(saved: Node) -> String:
|
|
@@ -394,7 +406,7 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
394
406
|
if not written.is_empty():
|
|
395
407
|
return written
|
|
396
408
|
|
|
397
|
-
var answer: Dictionary = {"ok": true, "deletedNodePath": at}
|
|
409
|
+
var answer: Dictionary = { "ok": true, "deletedNodePath": at }
|
|
398
410
|
if not went.is_empty():
|
|
399
411
|
answer["deletedWithIt"] = went
|
|
400
412
|
answer.merge(GdhKin.follow(scene_path, at, "", carried))
|
|
@@ -409,7 +421,7 @@ func duplicate_node(args: Dictionary) -> Dictionary:
|
|
|
409
421
|
var parent_path: String = str(args.get("parentNodePath", ""))
|
|
410
422
|
|
|
411
423
|
if node_path.is_empty() or new_name.is_empty():
|
|
412
|
-
return {"ok": false, "error": "Missing nodePath or newName"}
|
|
424
|
+
return { "ok": false, "error": "Missing nodePath or newName" }
|
|
413
425
|
|
|
414
426
|
var opened: Array = _open(scene_path, true)
|
|
415
427
|
var scene: GdhSceneFile = opened[0]
|
|
@@ -466,7 +478,9 @@ func duplicate_node(args: Dictionary) -> Dictionary:
|
|
|
466
478
|
if copy == null:
|
|
467
479
|
return "the copy would not be in the scene when it loads."
|
|
468
480
|
if copy.get_index() != placed[1]:
|
|
469
|
-
return
|
|
481
|
+
return (
|
|
482
|
+
"%s would load at index %d, not %d." % [new_path, copy.get_index(), placed[1]]
|
|
483
|
+
)
|
|
470
484
|
return ""
|
|
471
485
|
)
|
|
472
486
|
if not written.is_empty():
|
|
@@ -489,9 +503,9 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
489
503
|
var new_parent_path: String = str(args.get("newParentPath", ""))
|
|
490
504
|
|
|
491
505
|
if node_path.is_empty() or node_path == ".":
|
|
492
|
-
return {"ok": false, "error": "Cannot reparent root node"}
|
|
506
|
+
return { "ok": false, "error": "Cannot reparent root node" }
|
|
493
507
|
if new_parent_path.is_empty():
|
|
494
|
-
return {"ok": false, "error": "Missing newParentPath"}
|
|
508
|
+
return { "ok": false, "error": "Missing newParentPath" }
|
|
495
509
|
|
|
496
510
|
var opened: Array = _open(scene_path, true)
|
|
497
511
|
var scene: GdhSceneFile = opened[0]
|
|
@@ -505,7 +519,9 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
505
519
|
if not node:
|
|
506
520
|
return scene.refuse(scene.not_found("Node not found: " + node_path, node_path))
|
|
507
521
|
if not new_parent:
|
|
508
|
-
return scene.refuse(
|
|
522
|
+
return scene.refuse(
|
|
523
|
+
scene.not_found("New parent not found: " + new_parent_path, new_parent_path)
|
|
524
|
+
)
|
|
509
525
|
var fixed: String = scene.fixed(node)
|
|
510
526
|
if not fixed.is_empty():
|
|
511
527
|
return scene.refuse(fixed)
|
|
@@ -531,10 +547,12 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
531
547
|
var drawn_at: Dictionary = {}
|
|
532
548
|
for property: String in GdhPlacing.placing(node):
|
|
533
549
|
drawn_at[property] = _properties.read_at(node, property)
|
|
534
|
-
var held_at: Transform3D = (
|
|
550
|
+
var held_at: Transform3D = (
|
|
551
|
+
(node as Node3D).transform if node is Node3D else Transform3D.IDENTITY
|
|
552
|
+
)
|
|
535
553
|
var stored_before: Dictionary = GdhHeadlessCheck.stored(node)
|
|
536
|
-
# Unowned while it moves: a node taken out of the tree keeps its owner, and adding it under the
|
|
537
|
-
# parent warned that the owner was not above it. Node.reparent warns the same way.
|
|
554
|
+
# Unowned while it moves: a node taken out of the tree keeps its owner, and adding it under the
|
|
555
|
+
# new parent warned that the owner was not above it. Node.reparent warns the same way.
|
|
538
556
|
node.owner = null
|
|
539
557
|
old_parent.remove_child(node)
|
|
540
558
|
new_parent.add_child(node)
|
|
@@ -551,18 +569,21 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
551
569
|
for property: String in drawn_at:
|
|
552
570
|
var now: Variant = _properties.read_at(node, property)
|
|
553
571
|
if not GdhSame.same(now, drawn_at[property]):
|
|
554
|
-
also_changed[property] = {
|
|
572
|
+
also_changed[property] = {
|
|
573
|
+
"was": _readable(node, drawn_at[property]), "now": _readable(node, now),
|
|
574
|
+
}
|
|
555
575
|
var sheared: String = ""
|
|
556
576
|
if node is Node3D and GdhPlacing.sheared((node as Node3D).basis):
|
|
557
577
|
var spatial: Node3D = node
|
|
558
578
|
also_changed["transform"] = {
|
|
559
|
-
"was": _readable(node, held_at), "now": _readable(node, spatial.transform)
|
|
579
|
+
"was": _readable(node, held_at), "now": _readable(node, spatial.transform),
|
|
560
580
|
}
|
|
561
581
|
sheared = (
|
|
562
582
|
(
|
|
563
|
-
"%s keeps where it is drawn under %s, which is scaled unevenly across the way it
|
|
564
|
-
+ " so its basis is sheared: no position, rotation and scale describe
|
|
565
|
-
+ " what it holds. keepGlobalTransform false keeps its own
|
|
583
|
+
"%s keeps where it is drawn under %s, which is scaled unevenly across the way it "
|
|
584
|
+
+ "is turned, so its basis is sheared: no position, rotation and scale describe "
|
|
585
|
+
+ "it, and transform is what it holds. keepGlobalTransform false keeps its own "
|
|
586
|
+
+ "transform instead."
|
|
566
587
|
)
|
|
567
588
|
% [str(node.name), new_parent_path]
|
|
568
589
|
)
|
|
@@ -570,7 +591,10 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
570
591
|
var new_path: String = scene.path_of(node)
|
|
571
592
|
var index: int = node.get_index()
|
|
572
593
|
var carried: GdhPaths = GdhPaths.new()
|
|
573
|
-
var _rewritten: int = carried.carry(
|
|
594
|
+
var _rewritten: int = carried.carry(
|
|
595
|
+
scene,
|
|
596
|
+
{ old_path: new_path } if new_path != old_path else {},
|
|
597
|
+
)
|
|
574
598
|
var written: Dictionary = scene.write(
|
|
575
599
|
func(saved: Node) -> String:
|
|
576
600
|
var copy: Node = saved.get_node_or_null(new_path)
|
|
@@ -611,7 +635,10 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
611
635
|
var adding: Array = GdhGroups.asked(args, "addGroups")
|
|
612
636
|
var removing: Array = GdhGroups.asked(args, "removeGroups")
|
|
613
637
|
if properties.is_empty() and editable == null and adding.is_empty() and removing.is_empty():
|
|
614
|
-
return {
|
|
638
|
+
return {
|
|
639
|
+
"ok": false,
|
|
640
|
+
"error": "set takes properties, editableChildren, addGroups or removeGroups.",
|
|
641
|
+
}
|
|
615
642
|
|
|
616
643
|
var opened: Array = _open(scene_path, true)
|
|
617
644
|
var scene: GdhSceneFile = opened[0]
|
|
@@ -661,7 +688,7 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
661
688
|
for key: Variant in properties:
|
|
662
689
|
held[str(key)] = _readable(node, _properties.read_at(node, str(key)))
|
|
663
690
|
var new_path: String = scene.path_of(node)
|
|
664
|
-
var answer: Dictionary = {"ok": true, "nodePath": new_path, "properties": held}
|
|
691
|
+
var answer: Dictionary = { "ok": true, "nodePath": new_path, "properties": held }
|
|
665
692
|
_properties.note_write(answer, node)
|
|
666
693
|
var shared: Dictionary = {}
|
|
667
694
|
for key: Variant in properties:
|
|
@@ -679,7 +706,10 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
679
706
|
named.append(property)
|
|
680
707
|
var source: String = scene.values_from(node, old_path)
|
|
681
708
|
var carried: GdhPaths = GdhPaths.new()
|
|
682
|
-
var _rewritten: int = carried.carry(
|
|
709
|
+
var _rewritten: int = carried.carry(
|
|
710
|
+
scene,
|
|
711
|
+
{ old_path: new_path } if new_path != old_path else {},
|
|
712
|
+
)
|
|
683
713
|
var id: int = node.get_instance_id()
|
|
684
714
|
var written: Dictionary = scene.write(
|
|
685
715
|
func(saved: Node) -> String:
|
|
@@ -687,9 +717,13 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
687
717
|
if unkept.is_empty():
|
|
688
718
|
unkept = GdhGroups.unkept(saved.get_node(new_path), adding, removing)
|
|
689
719
|
if unkept.is_empty() and editable != null:
|
|
690
|
-
if
|
|
720
|
+
if (
|
|
721
|
+
saved.is_editable_instance(saved.get_node(new_path))
|
|
722
|
+
!= GdhRead.as_bool(editable)
|
|
723
|
+
):
|
|
691
724
|
return (
|
|
692
|
-
"%s would load with editableChildren %s."
|
|
725
|
+
"%s would load with editableChildren %s."
|
|
726
|
+
% [new_path, not GdhRead.as_bool(editable)]
|
|
693
727
|
)
|
|
694
728
|
return unkept
|
|
695
729
|
)
|
|
@@ -722,16 +756,16 @@ static func _note_loads_as(answer: Dictionary, scene: GdhSceneFile, at: String)
|
|
|
722
756
|
if scene.loads_untooled.has(at):
|
|
723
757
|
answer["loadsAsNote"] = (
|
|
724
758
|
(
|
|
725
|
-
"The file holds %s as given, which the editor shows. %s's script is not a tool, so
|
|
726
|
-
+ "game it runs as the scene loads and changes %s to what loadsAs gives."
|
|
759
|
+
"The file holds %s as given, which the editor shows. %s's script is not a tool, so "
|
|
760
|
+
+ "in the game it runs as the scene loads and changes %s to what loadsAs gives."
|
|
727
761
|
)
|
|
728
762
|
% [", ".join(names), at, pronoun]
|
|
729
763
|
)
|
|
730
764
|
else:
|
|
731
765
|
answer["loadsAsNote"] = (
|
|
732
766
|
(
|
|
733
|
-
"The file holds %s as given. %s's script runs as the scene loads, in the editor
|
|
734
|
-
+ "the game, and changes %s to what loadsAs gives."
|
|
767
|
+
"The file holds %s as given. %s's script runs as the scene loads, in the editor "
|
|
768
|
+
+ "and in the game, and changes %s to what loadsAs gives."
|
|
735
769
|
)
|
|
736
770
|
% [", ".join(names), at, pronoun]
|
|
737
771
|
)
|
|
@@ -773,8 +807,8 @@ static func _note_left_out(
|
|
|
773
807
|
else:
|
|
774
808
|
answer["notStoredNote"] = (
|
|
775
809
|
(
|
|
776
|
-
"%s now %s what %s gives this node, so the file does not store %s, and a change
|
|
777
|
-
+ "this node."
|
|
810
|
+
"%s now %s what %s gives this node, so the file does not store %s, and a change "
|
|
811
|
+
+ "there reaches this node."
|
|
778
812
|
)
|
|
779
813
|
% [names, verb, source, pronoun]
|
|
780
814
|
)
|
|
@@ -806,7 +840,7 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
806
840
|
var at: String = scene.path_of(node)
|
|
807
841
|
var here: Array = set_here.keys()
|
|
808
842
|
here.sort()
|
|
809
|
-
var answer: Dictionary = {"ok": true, "nodePath": at, "properties": props, "setHere": here}
|
|
843
|
+
var answer: Dictionary = { "ok": true, "nodePath": at, "properties": props, "setHere": here }
|
|
810
844
|
# A Node3D stores only its transform, a matrix, while a set takes the position, rotation and
|
|
811
845
|
# scale the inspector shows, so a node turned 45 degrees down read back as nine numbers. A
|
|
812
846
|
# sheared one has no such three, and is left to its transform.
|
|
@@ -827,7 +861,9 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
827
861
|
# A player's getter answers Master for a bus the layout does not have, while the file keeps the
|
|
828
862
|
# name, so a player left on a renamed or removed bus read as one put on Master.
|
|
829
863
|
var bus: String = str(set_here.get("bus", ""))
|
|
830
|
-
var plays: bool =
|
|
864
|
+
var plays: bool = (
|
|
865
|
+
node is AudioStreamPlayer or node is AudioStreamPlayer2D or node is AudioStreamPlayer3D
|
|
866
|
+
)
|
|
831
867
|
if plays and not bus.is_empty() and AudioServer.get_bus_index(bus) == -1:
|
|
832
868
|
props["bus"] = bus
|
|
833
869
|
var buses: PackedStringArray = []
|
|
@@ -835,10 +871,15 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
835
871
|
var _added: bool = buses.append(AudioServer.get_bus_name(index))
|
|
836
872
|
answer["busNote"] = (
|
|
837
873
|
(
|
|
838
|
-
"%s names the bus %s, which the project's bus layout does not have, so it plays on
|
|
839
|
-
+ "the buses are %s."
|
|
874
|
+
"%s names the bus %s, which the project's bus layout does not have, so it plays on "
|
|
875
|
+
+ "%s; the buses are %s."
|
|
840
876
|
)
|
|
841
|
-
% [
|
|
877
|
+
% [
|
|
878
|
+
str(node.name) if at == "." else at,
|
|
879
|
+
bus,
|
|
880
|
+
AudioServer.get_bus_name(0),
|
|
881
|
+
", ".join(buses),
|
|
882
|
+
]
|
|
842
883
|
)
|
|
843
884
|
# A mask reads as a number, and which layers it holds, under the names the project gives them,
|
|
844
885
|
# is what a caller reads it for.
|
|
@@ -902,7 +943,7 @@ func save_scene(args: Dictionary) -> Dictionary:
|
|
|
902
943
|
if target_path != scene_path:
|
|
903
944
|
var unsaved: String = GdhSceneFile.unsaved_in_editor(target_path)
|
|
904
945
|
if not unsaved.is_empty():
|
|
905
|
-
return {"ok": false, "error": unsaved}
|
|
946
|
+
return { "ok": false, "error": unsaved }
|
|
906
947
|
|
|
907
948
|
var open_root: Node = null
|
|
908
949
|
var open_roots: Array[Node] = []
|
|
@@ -922,7 +963,9 @@ func save_scene(args: Dictionary) -> Dictionary:
|
|
|
922
963
|
var packed: PackedScene = PackedScene.new()
|
|
923
964
|
var packing: Error = packed.pack(open_root)
|
|
924
965
|
if packing != OK:
|
|
925
|
-
return {
|
|
966
|
+
return {
|
|
967
|
+
"ok": false, "error": "Failed to pack %s: %s" % [scene_path, error_string(packing)],
|
|
968
|
+
}
|
|
926
969
|
written = GdhSceneFile.save_packed(packed, target_path)
|
|
927
970
|
else:
|
|
928
971
|
var scene: GdhSceneFile = GdhSceneFile.new()
|
|
@@ -938,7 +981,7 @@ func save_scene(args: Dictionary) -> Dictionary:
|
|
|
938
981
|
"ok": true,
|
|
939
982
|
"scenePath": scene_path,
|
|
940
983
|
"savedPath": target_path,
|
|
941
|
-
"from": "editor" if open_root != null else "file"
|
|
984
|
+
"from": "editor" if open_root != null else "file",
|
|
942
985
|
}
|
|
943
986
|
|
|
944
987
|
|
|
@@ -952,16 +995,19 @@ func _save_open_scene(scene_path: String) -> Dictionary:
|
|
|
952
995
|
EditorInterface.open_scene_from_path(scene_path)
|
|
953
996
|
var now: Node = EditorInterface.get_edited_scene_root()
|
|
954
997
|
if now == null or now.scene_file_path != scene_path:
|
|
955
|
-
return {"ok": false, "error": "The editor did not switch to %s to save it." % scene_path}
|
|
998
|
+
return { "ok": false, "error": "The editor did not switch to %s to save it." % scene_path }
|
|
956
999
|
var saving: Error = EditorInterface.save_scene()
|
|
957
1000
|
if not previous.is_empty() and previous != scene_path:
|
|
958
1001
|
EditorInterface.open_scene_from_path(previous)
|
|
959
1002
|
if saving != OK:
|
|
960
|
-
return {
|
|
1003
|
+
return {
|
|
1004
|
+
"ok": false,
|
|
1005
|
+
"error": "The editor failed to save %s: %s" % [scene_path, error_string(saving)],
|
|
1006
|
+
}
|
|
961
1007
|
return {
|
|
962
1008
|
"ok": true,
|
|
963
1009
|
"scenePath": scene_path,
|
|
964
1010
|
"savedPath": scene_path,
|
|
965
1011
|
"from": "editor",
|
|
966
|
-
"unsavedInEditor": GdhSceneFile.unsaved(scene_path)
|
|
1012
|
+
"unsavedInEditor": GdhSceneFile.unsaved(scene_path),
|
|
967
1013
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## The node paths a GDScript names in its own source, from the node it is on: `$Path`, `%Unique`,
|
|
5
4
|
## and a string handed to get_node, get_node_or_null or has_node called on itself. Read from the
|
|
6
5
|
## tokens rather than the lines, so a `$` or a `%` inside a string or a comment names nothing, and
|
|
7
6
|
## a `%` after an operand is the remainder of a division rather than a unique name.
|
|
8
7
|
|
|
9
|
-
enum {
|
|
8
|
+
enum {
|
|
9
|
+
NAME,
|
|
10
|
+
STRING,
|
|
11
|
+
MARK,
|
|
12
|
+
}
|
|
10
13
|
|
|
11
14
|
## The calls that take a path from the node they are called on.
|
|
12
15
|
const CALLS: Array[String] = ["get_node", "get_node_or_null", "has_node"]
|
|
13
16
|
## The words after which a `%` starts an operand.
|
|
14
17
|
const OPERAND_AFTER: Array[String] = [
|
|
15
|
-
"return", "and", "or", "not", "in", "if", "elif", "while", "await", "match", "when", "else"
|
|
18
|
+
"return", "and", "or", "not", "in", "if", "elif", "while", "await", "match", "when", "else",
|
|
16
19
|
]
|
|
17
20
|
## The marks after which a `%` starts an operand: an opening bracket, a separator or an operator.
|
|
18
21
|
const OPERAND_AFTER_MARKS: String = "([{,=:;+-*/<>!&|^~%"
|
|
@@ -33,10 +36,14 @@ static func named(source: String) -> Array[Dictionary]:
|
|
|
33
36
|
elif _is_mark(token, "%") and _starts_operand(tokens, at):
|
|
34
37
|
path = _path_from(tokens, at)
|
|
35
38
|
elif token[0] == NAME and CALLS.has(str(token[1])) and _on_itself(tokens, at):
|
|
36
|
-
if
|
|
39
|
+
if (
|
|
40
|
+
at + 2 < tokens.size()
|
|
41
|
+
and _is_mark(tokens[at + 1], "(")
|
|
42
|
+
and tokens[at + 2][0] == STRING
|
|
43
|
+
):
|
|
37
44
|
path = str(tokens[at + 2][1])
|
|
38
45
|
if not path.is_empty():
|
|
39
|
-
found.append({"line": token[2], "path": path})
|
|
46
|
+
found.append({ "line": token[2], "path": path })
|
|
40
47
|
return found
|
|
41
48
|
|
|
42
49
|
|
|
@@ -58,7 +65,8 @@ static func _path_from(tokens: Array[Array], at: int) -> String:
|
|
|
58
65
|
return "/".join(parts)
|
|
59
66
|
|
|
60
67
|
|
|
61
|
-
## The name of a path at [param at], as [its text, where the next token is], or [] when there is
|
|
68
|
+
## The name of a path at [param at], as [its text, where the next token is], or [] when there is
|
|
69
|
+
## none.
|
|
62
70
|
static func _part_at(tokens: Array[Array], at: int) -> Array:
|
|
63
71
|
var unique: String = ""
|
|
64
72
|
if at < tokens.size() and _is_mark(tokens[at], "%"):
|
|
@@ -112,7 +120,10 @@ static func _tokens(source: String) -> Array[Array]:
|
|
|
112
120
|
at += 1
|
|
113
121
|
elif c in " \t\r":
|
|
114
122
|
at += 1
|
|
115
|
-
elif
|
|
123
|
+
elif (
|
|
124
|
+
QUOTES.contains(c)
|
|
125
|
+
or ("r^&".contains(c) and QUOTES.contains(next) and not next.is_empty())
|
|
126
|
+
):
|
|
116
127
|
var quoted: Array = _string(source, at if QUOTES.contains(c) else at + 1)
|
|
117
128
|
tokens.append([STRING, quoted[0], line])
|
|
118
129
|
line += str(quoted[0]).count("\n")
|
|
@@ -133,7 +144,8 @@ static func _tokens(source: String) -> Array[Array]:
|
|
|
133
144
|
|
|
134
145
|
|
|
135
146
|
## The string whose opening quote is at [param at], as [what is between its quotes, where it ends].
|
|
136
|
-
## A backslash keeps the character after it inside the string, raw or not, as the tokenizer reads
|
|
147
|
+
## A backslash keeps the character after it inside the string, raw or not, as the tokenizer reads
|
|
148
|
+
## it.
|
|
137
149
|
static func _string(source: String, at: int) -> Array:
|
|
138
150
|
var quote: String = source[at]
|
|
139
151
|
var closing: String = quote.repeat(3) if source.substr(at, 3) == quote.repeat(3) else quote
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## Project settings read from the open editor, for a caller that asked for the editor's answer, and
|
|
5
4
|
## taken up by it after a write to the file.
|
|
6
5
|
##
|
|
@@ -29,7 +28,9 @@ func get_project_setting(args: Dictionary) -> Dictionary:
|
|
|
29
28
|
|
|
30
29
|
var setting: String = str(args.get("setting", ""))
|
|
31
30
|
if setting.is_empty():
|
|
32
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
"ok": false, "error": "setting is required, or prefix to read every setting under one",
|
|
33
|
+
}
|
|
33
34
|
|
|
34
35
|
var result: Dictionary = GdhSettingReads.one(setting, _values)
|
|
35
36
|
result["ok"] = true
|
|
@@ -46,11 +47,11 @@ func adopt_project_settings(args: Dictionary) -> Dictionary:
|
|
|
46
47
|
var file: ConfigFile = ConfigFile.new()
|
|
47
48
|
var loaded: Error = file.load("res://project.godot")
|
|
48
49
|
if loaded != OK:
|
|
49
|
-
return {"ok": false, "error": "Could not read project.godot: " + error_string(loaded)}
|
|
50
|
+
return { "ok": false, "error": "Could not read project.godot: " + error_string(loaded) }
|
|
50
51
|
var adopted: Array[String] = []
|
|
51
52
|
var named: Variant = args.get("settings", [])
|
|
52
53
|
if not named is Array:
|
|
53
|
-
return {"ok": false, "error": "settings must be a list of setting names"}
|
|
54
|
+
return { "ok": false, "error": "settings must be a list of setting names" }
|
|
54
55
|
var settings: Array = named
|
|
55
56
|
for entry: Variant in settings:
|
|
56
57
|
var setting: String = str(entry)
|
|
@@ -66,19 +67,21 @@ func adopt_project_settings(args: Dictionary) -> Dictionary:
|
|
|
66
67
|
else:
|
|
67
68
|
ProjectSettings.set_setting(setting, null)
|
|
68
69
|
adopted.append(setting)
|
|
69
|
-
return {"ok": true, "adopted": adopted}
|
|
70
|
+
return { "ok": true, "adopted": adopted }
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
## Loads the bus layout the project names into the editor's audio server, after something else
|
|
73
74
|
## wrote it: the editor holds the layout it loaded and saves that one when its audio panel changes.
|
|
74
75
|
func adopt_audio_bus_layout(_args: Dictionary) -> Dictionary:
|
|
75
76
|
var path: String = str(
|
|
76
|
-
ProjectSettings.get_setting(
|
|
77
|
+
ProjectSettings.get_setting(
|
|
78
|
+
"audio/buses/default_bus_layout", "res://default_bus_layout.tres"
|
|
79
|
+
)
|
|
77
80
|
)
|
|
78
81
|
var layout: AudioBusLayout = ResourceLoader.load(
|
|
79
82
|
path, "AudioBusLayout", ResourceLoader.CACHE_MODE_REPLACE
|
|
80
83
|
)
|
|
81
84
|
if layout == null:
|
|
82
|
-
return {"ok": false, "error": "Could not load the bus layout at " + path}
|
|
85
|
+
return { "ok": false, "error": "Could not load the bus layout at " + path }
|
|
83
86
|
AudioServer.set_bus_layout(layout)
|
|
84
|
-
return {"ok": true, "layout": path}
|
|
87
|
+
return { "ok": true, "layout": path }
|