gdharness 1.3.8 → 1.3.10
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 +4668 -3229
- package/build/godot/addons/gdharness_editor/animation_reads.gd +115 -4
- package/build/godot/addons/gdharness_editor/declared_values.gd +272 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +115 -18
- package/build/godot/addons/gdharness_editor/headless_check.gd +49 -2
- package/build/godot/addons/gdharness_editor/property_paths.gd +89 -16
- package/build/godot/addons/gdharness_editor/property_values.gd +180 -121
- package/build/godot/addons/gdharness_editor/reading.gd +112 -6
- package/build/godot/addons/gdharness_editor/scene_file.gd +117 -174
- package/build/godot/addons/gdharness_editor/scene_uids.gd +107 -0
- package/build/godot/addons/gdharness_editor/scene_uids.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/serialisation.gd +19 -29
- package/build/godot/addons/gdharness_editor/setting_reads.gd +121 -0
- package/build/godot/addons/gdharness_editor/setting_reads.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tool_executor.gd +39 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +263 -0
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +49 -414
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +464 -0
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +37 -0
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +221 -0
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +162 -483
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -0
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +8 -4
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +2 -1
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +10 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +151 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +7 -30
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +190 -38
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +485 -0
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd +128 -1
- package/build/godot/addons/gdharness_runtime/call_errors.gd +8 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +272 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +112 -6
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +35 -3
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +5 -0
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +188 -30
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +107 -104
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +17 -0
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +13 -4
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +320 -27
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +165 -20
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +49 -3
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +31 -2
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +250 -10
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +279 -11
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +27 -0
- package/build/godot/addons/gdharness_runtime/serialisation.gd +19 -29
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +128 -1
- package/build/godot/operations/animation_reads.gd +115 -4
- package/build/godot/operations/audio_buses.gd +93 -7
- package/build/godot/operations/classdb_queries.gd +119 -7
- package/build/godot/operations/declared_values.gd +272 -0
- package/build/godot/operations/dependencies.gd +154 -41
- package/build/godot/operations/editor_tools.gd +29 -13
- package/build/godot/operations/export_presets.gd +602 -4
- package/build/godot/operations/file_reads.gd +115 -18
- package/build/godot/operations/gdscript_analysis.gd +14 -0
- package/build/godot/operations/gdscript_authoring.gd +12 -2
- package/build/godot/operations/godot_operations.gd +18 -2
- package/build/godot/operations/import_pipeline.gd +20 -221
- package/build/godot/operations/input_actions.gd +127 -51
- package/build/godot/operations/project_config.gd +113 -68
- package/build/godot/operations/project_diagnostics.gd +4 -0
- package/build/godot/operations/project_validation.gd +266 -0
- package/build/godot/operations/property_paths.gd +89 -16
- package/build/godot/operations/property_values.gd +180 -121
- package/build/godot/operations/reading.gd +112 -6
- package/build/godot/operations/scene_connections.gd +222 -0
- package/build/godot/operations/serialisation.gd +19 -29
- package/build/godot/operations/setting_reads.gd +121 -0
- package/build/godot/operations/value_shapes.gd +128 -1
- package/build/godot/tests/listed.gd +20 -13
- package/build/keeper.js +1402 -707
- package/package.json +1 -1
|
@@ -22,6 +22,22 @@ static func read(scene: GdhSceneFile, node: Node) -> Dictionary:
|
|
|
22
22
|
return answer
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
## The groups of [param node], just added to this scene and put in [param adding] there, in the shape
|
|
26
|
+
## [method read] answers: this scene's file does not hold the node until it is saved.
|
|
27
|
+
static func read_added(node: Node, adding: Array) -> Dictionary:
|
|
28
|
+
var all: Array = _sorted(node.get_groups())
|
|
29
|
+
if all.is_empty():
|
|
30
|
+
return {}
|
|
31
|
+
var answer: Dictionary = {"groups": all}
|
|
32
|
+
var here: Array = []
|
|
33
|
+
for group: String in _sorted(adding):
|
|
34
|
+
if not here.has(group):
|
|
35
|
+
here.append(group)
|
|
36
|
+
if here.size() != all.size():
|
|
37
|
+
answer["groupsHere"] = here
|
|
38
|
+
return answer
|
|
39
|
+
|
|
40
|
+
|
|
25
41
|
## The group names under [param key] of [param args], which the server has checked are strings, or
|
|
26
42
|
## none when not given.
|
|
27
43
|
static func asked(args: Dictionary, key: String) -> Array:
|
|
@@ -7,6 +7,7 @@ extends RefCounted
|
|
|
7
7
|
const GdhFileReads = preload("../file_reads.gd")
|
|
8
8
|
const GdhPaths = preload("scene_paths.gd")
|
|
9
9
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
10
|
+
const GdhSceneUids = preload("../scene_uids.gd")
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
## Why a create at [param scene_path] is refused when a file is there already, which saving over
|
|
@@ -87,14 +88,14 @@ static func inherit(scene_path: String, base_path: String, root_name: String, sc
|
|
|
87
88
|
"",
|
|
88
89
|
(
|
|
89
90
|
'[ext_resource type="PackedScene"%s path="%s" id="1_base"]'
|
|
90
|
-
% [
|
|
91
|
+
% [GdhSceneUids.reference_uid(base_path), base_path]
|
|
91
92
|
),
|
|
92
93
|
]
|
|
93
94
|
if script != null:
|
|
94
95
|
var _resourced: bool = lines.append(
|
|
95
96
|
(
|
|
96
97
|
'[ext_resource type="Script"%s path="%s" id="2_script"]'
|
|
97
|
-
% [
|
|
98
|
+
% [GdhSceneUids.reference_uid(script.resource_path), script.resource_path]
|
|
98
99
|
)
|
|
99
100
|
)
|
|
100
101
|
lines.append_array(["", '[node name="%s" instance=ExtResource("1_base")]' % name])
|
|
@@ -118,13 +119,16 @@ static func inherit(scene_path: String, base_path: String, root_name: String, sc
|
|
|
118
119
|
"%s did not load back as a scene inheriting %s, so it was removed." % [scene_path, base_path]
|
|
119
120
|
}
|
|
120
121
|
GdhSceneFile.refresh(scene_path)
|
|
121
|
-
|
|
122
|
+
var answer: Dictionary = {
|
|
122
123
|
"ok": true,
|
|
123
124
|
"scenePath": scene_path,
|
|
124
125
|
"inherits": base_path,
|
|
125
126
|
"rootNodeType": root_type,
|
|
126
127
|
"rootNodeName": name
|
|
127
128
|
}
|
|
129
|
+
if script != null:
|
|
130
|
+
answer["script"] = script.resource_path
|
|
131
|
+
return answer
|
|
128
132
|
|
|
129
133
|
|
|
130
134
|
## Makes the children of [param node], an instance in [param scene], editable or not, answering why
|
|
@@ -176,7 +180,7 @@ static func follow(changed: String, old_path: String, new_path: String, carried:
|
|
|
176
180
|
var moves: Dictionary = {}
|
|
177
181
|
if _inherits(scene_path, changed, texts):
|
|
178
182
|
moves[old_path] = new_path
|
|
179
|
-
var read: Dictionary = GdhFileReads.scene_tree(scene_path, -1, false)
|
|
183
|
+
var read: Dictionary = GdhFileReads.scene_tree(scene_path, -1, false, false)
|
|
180
184
|
if not read.get("ok", false):
|
|
181
185
|
not_followed.append({"scenePath": scene_path, "reason": str(read.get("error", ""))})
|
|
182
186
|
continue
|
|
@@ -14,6 +14,7 @@ enum { UNCHANGED, REWRITTEN, DELETED }
|
|
|
14
14
|
const GdhHeadlessCheck = preload("../headless_check.gd")
|
|
15
15
|
const GdhRead = preload("../reading.gd")
|
|
16
16
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
17
|
+
const GdhSceneUids = preload("../scene_uids.gd")
|
|
17
18
|
## PROPERTY_HINT_NO_NODEPATH, which marks a property the editor's rename skips. It is not bound for
|
|
18
19
|
## scripts, and follows PROPERTY_HINT_ONESHOT in the engine's list.
|
|
19
20
|
const HINT_NO_NODEPATH: int = PROPERTY_HINT_ONESHOT + 1
|
|
@@ -127,7 +128,7 @@ func put_back(scene_path: String, why: String = "") -> void:
|
|
|
127
128
|
func answer() -> Dictionary:
|
|
128
129
|
for file: String in _homes:
|
|
129
130
|
var resource: Resource = _homes[file]
|
|
130
|
-
var saving: Error =
|
|
131
|
+
var saving: Error = GdhSceneUids.save_keeping_uid(resource, file)
|
|
131
132
|
if saving == OK:
|
|
132
133
|
if Engine.is_editor_hint():
|
|
133
134
|
EditorInterface.get_resource_filesystem().update_file(file)
|
|
@@ -171,6 +171,16 @@ static func place(node: Node, asked: Variant) -> void:
|
|
|
171
171
|
node.get_parent().move_child(node, GdhRead.as_int(asked))
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
## Whether [param basis] has axes that are not at right angles, which a node kept in place under a
|
|
175
|
+
## parent scaled unevenly across its turn ends up with. A Node2D says so through skew; a Node3D has
|
|
176
|
+
## no property for it, and its position, rotation and scale read as if the axes were square.
|
|
177
|
+
static func sheared(basis: Basis) -> bool:
|
|
178
|
+
var x: Vector3 = basis.x.normalized()
|
|
179
|
+
var y: Vector3 = basis.y.normalized()
|
|
180
|
+
var z: Vector3 = basis.z.normalized()
|
|
181
|
+
return absf(x.dot(y)) > 0.0001 or absf(x.dot(z)) > 0.0001 or absf(y.dot(z)) > 0.0001
|
|
182
|
+
|
|
183
|
+
|
|
174
184
|
## The properties that hold where [param node] is drawn, which a reparent keeping it in place
|
|
175
185
|
## changes: a Control keeps only its position, as Node.reparent keeps it.
|
|
176
186
|
static func placing(node: Node) -> Array[String]:
|
|
@@ -3,17 +3,20 @@ extends Node
|
|
|
3
3
|
|
|
4
4
|
## Scenes and their nodes, edited in the open editor and read back from what it holds.
|
|
5
5
|
|
|
6
|
+
const GdhDeclared = preload("../declared_values.gd")
|
|
6
7
|
const GdhFileReads = preload("../file_reads.gd")
|
|
7
8
|
const GdhRead = preload("../reading.gd")
|
|
8
9
|
const GdhGroups = preload("scene_groups.gd")
|
|
10
|
+
const GdhHeadlessCheck = preload("../headless_check.gd")
|
|
9
11
|
const GdhKin = preload("scene_kin.gd")
|
|
12
|
+
const GdhNavigationBake = preload("navigation_bake.gd")
|
|
10
13
|
const GdhPaths = preload("scene_paths.gd")
|
|
11
14
|
const GdhPlacing = preload("scene_placing.gd")
|
|
12
15
|
const GdhPropertyValues = preload("../property_values.gd")
|
|
13
|
-
const GdhResourceTools = preload("resource_tools.gd")
|
|
14
16
|
const GdhSame = preload("../same_values.gd")
|
|
15
17
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
16
18
|
const GdhSerialisation = preload("../serialisation.gd")
|
|
19
|
+
const GdhTilesetTools = preload("tileset_tools.gd")
|
|
17
20
|
|
|
18
21
|
var _editor_plugin: EditorPlugin = null
|
|
19
22
|
var _values: GdhSerialisation = GdhSerialisation.new()
|
|
@@ -204,6 +207,8 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
204
207
|
var answer: Dictionary = {
|
|
205
208
|
"ok": true, "scenePath": scene_path, "rootNodeType": asked_type, "rootNodeName": root_node_name
|
|
206
209
|
}
|
|
210
|
+
if not script_path.is_empty():
|
|
211
|
+
answer["script"] = script_path
|
|
207
212
|
if not had_folder:
|
|
208
213
|
answer["madeFolder"] = scene_path.get_base_dir()
|
|
209
214
|
return answer
|
|
@@ -214,10 +219,11 @@ func list_scene_nodes(args: Dictionary) -> Dictionary:
|
|
|
214
219
|
var scene_path: String = GdhSceneFile.res_path(project_path, str(args.get("scenePath", "")))
|
|
215
220
|
var depth: int = GdhRead.as_int(args.get("depth", -1), -1)
|
|
216
221
|
var include_properties: bool = GdhRead.as_bool(args.get("includeProperties", false))
|
|
222
|
+
var include_defaults: bool = GdhRead.as_bool(args.get("includeDefaults", false))
|
|
217
223
|
|
|
218
224
|
# Read as a headless engine reads it with no editor connected, so the answer does not depend on
|
|
219
225
|
# whether one is.
|
|
220
|
-
var read: Dictionary = GdhFileReads.scene_tree(scene_path, depth, include_properties)
|
|
226
|
+
var read: Dictionary = GdhFileReads.scene_tree(scene_path, depth, include_properties, include_defaults)
|
|
221
227
|
if read.get("ok", false):
|
|
222
228
|
read["unsavedInEditor"] = GdhSceneFile.unsaved(scene_path)
|
|
223
229
|
return read
|
|
@@ -285,6 +291,9 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
285
291
|
return scene.refuse("Nothing was written: " + unsettled)
|
|
286
292
|
scene.asked(new_node, properties.keys())
|
|
287
293
|
var unwritten: String = _properties.write_all(new_node, properties)
|
|
294
|
+
scene.asked(new_node, _properties.moved_by_engine.keys())
|
|
295
|
+
if not unwritten.is_empty():
|
|
296
|
+
unwritten = GdhHeadlessCheck.setter_ran(new_node, _properties.unheld, unwritten)
|
|
288
297
|
if unwritten.is_empty():
|
|
289
298
|
unwritten = GdhGroups.change(scene, new_node, adding, [])
|
|
290
299
|
if not unwritten.is_empty():
|
|
@@ -299,9 +308,14 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
299
308
|
"nodePath": new_path,
|
|
300
309
|
"index": new_node.get_index(),
|
|
301
310
|
}
|
|
311
|
+
if not properties.is_empty():
|
|
312
|
+
var held: Dictionary = {}
|
|
313
|
+
for key: Variant in properties:
|
|
314
|
+
held[str(key)] = _readable(new_node, _properties.read_at(new_node, str(key)))
|
|
315
|
+
answer["properties"] = held
|
|
302
316
|
_properties.note_write(answer, new_node)
|
|
303
317
|
if not adding.is_empty():
|
|
304
|
-
answer.merge(GdhGroups.
|
|
318
|
+
answer.merge(GdhGroups.read_added(new_node, adding))
|
|
305
319
|
var written: Dictionary = scene.write(
|
|
306
320
|
func(saved: Node) -> String:
|
|
307
321
|
var unkept: String = _kept(
|
|
@@ -503,6 +517,7 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
503
517
|
var drawn_at: Dictionary = {}
|
|
504
518
|
for property: String in GdhPlacing.placing(node):
|
|
505
519
|
drawn_at[property] = _properties.read_at(node, property)
|
|
520
|
+
var held_at: Transform3D = (node as Node3D).transform if node is Node3D else Transform3D.IDENTITY
|
|
506
521
|
# Unowned while it moves: a node taken out of the tree keeps its owner, and adding it under the new
|
|
507
522
|
# parent warned that the owner was not above it. Node.reparent warns the same way.
|
|
508
523
|
node.owner = null
|
|
@@ -517,6 +532,20 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
517
532
|
var now: Variant = _properties.read_at(node, property)
|
|
518
533
|
if not GdhSame.same(now, drawn_at[property]):
|
|
519
534
|
also_changed[property] = {"was": _readable(node, drawn_at[property]), "now": _readable(node, now)}
|
|
535
|
+
var sheared: String = ""
|
|
536
|
+
if node is Node3D and GdhPlacing.sheared((node as Node3D).basis):
|
|
537
|
+
var spatial: Node3D = node
|
|
538
|
+
also_changed["transform"] = {
|
|
539
|
+
"was": _readable(node, held_at), "now": _readable(node, spatial.transform)
|
|
540
|
+
}
|
|
541
|
+
sheared = (
|
|
542
|
+
(
|
|
543
|
+
"%s keeps where it is drawn under %s, which is scaled unevenly across the way it is turned,"
|
|
544
|
+
+ " so its basis is sheared: no position, rotation and scale describe it, and transform is"
|
|
545
|
+
+ " what it holds. keepGlobalTransform false keeps its own transform instead."
|
|
546
|
+
)
|
|
547
|
+
% [str(node.name), new_parent_path]
|
|
548
|
+
)
|
|
520
549
|
|
|
521
550
|
var new_path: String = scene.path_of(node)
|
|
522
551
|
var index: int = node.get_index()
|
|
@@ -546,6 +575,8 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
546
575
|
}
|
|
547
576
|
if not also_changed.is_empty():
|
|
548
577
|
answer["alsoChanged"] = also_changed
|
|
578
|
+
if not sheared.is_empty():
|
|
579
|
+
answer["sheared"] = sheared
|
|
549
580
|
if new_path != old_path:
|
|
550
581
|
answer.merge(GdhKin.follow(scene_path, old_path, new_path, carried))
|
|
551
582
|
return answer
|
|
@@ -571,9 +602,8 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
571
602
|
var node: Node = GdhSceneFile.node_at(scene.root, node_path)
|
|
572
603
|
if not node:
|
|
573
604
|
return scene.refuse(scene.not_found("Node not found: " + node_path, node_path))
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
return scene.refuse(foreign)
|
|
605
|
+
# A rename is refused for the scene that makes the node before anything else, since editable
|
|
606
|
+
# children would let this scene keep values on it and still never rename it.
|
|
577
607
|
if properties.has("name") and node != scene.root:
|
|
578
608
|
var fixed: String = scene.fixed(node)
|
|
579
609
|
if not fixed.is_empty():
|
|
@@ -581,6 +611,9 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
581
611
|
var unusable: String = scene.unusable_name(node.get_parent(), str(properties["name"]), node)
|
|
582
612
|
if not unusable.is_empty():
|
|
583
613
|
return scene.refuse(unusable)
|
|
614
|
+
var foreign: String = scene.foreign(node)
|
|
615
|
+
if not foreign.is_empty():
|
|
616
|
+
return scene.refuse(foreign)
|
|
584
617
|
|
|
585
618
|
var dropped: Dictionary = {}
|
|
586
619
|
if editable != null:
|
|
@@ -593,8 +626,13 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
593
626
|
var old_path: String = scene.path_of(node)
|
|
594
627
|
scene.asked(node, properties.keys())
|
|
595
628
|
var refused_property: String = _properties.write_all(node, properties)
|
|
629
|
+
scene.asked(node, _properties.moved_by_engine.keys())
|
|
596
630
|
if not refused_property.is_empty():
|
|
597
|
-
return scene.refuse(
|
|
631
|
+
return scene.refuse(
|
|
632
|
+
GdhHeadlessCheck.setter_ran(
|
|
633
|
+
node, _properties.unheld, GdhSceneFile.missing_on(node, refused_property)
|
|
634
|
+
)
|
|
635
|
+
)
|
|
598
636
|
var ungrouped: String = GdhGroups.change(scene, node, adding, removing)
|
|
599
637
|
if not ungrouped.is_empty():
|
|
600
638
|
return scene.refuse(ungrouped)
|
|
@@ -607,6 +645,12 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
607
645
|
_properties.note_write(answer, node)
|
|
608
646
|
if not adding.is_empty() or not removing.is_empty():
|
|
609
647
|
answer["groups"] = GdhGroups.read(scene, node).get("groups", [])
|
|
648
|
+
var named: Array[String] = []
|
|
649
|
+
for key: Variant in properties:
|
|
650
|
+
var property: String = str(key).get_slice(":", 0)
|
|
651
|
+
if property != "name" and property in node and not named.has(property):
|
|
652
|
+
named.append(property)
|
|
653
|
+
var source: String = scene.values_from(node, old_path)
|
|
610
654
|
var carried: GdhPaths = GdhPaths.new()
|
|
611
655
|
var _rewritten: int = carried.carry(scene, {old_path: new_path} if new_path != old_path else {})
|
|
612
656
|
var written: Dictionary = scene.write(
|
|
@@ -630,11 +674,48 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
630
674
|
if not dropped.is_empty():
|
|
631
675
|
answer["droppedValues"] = dropped["values"]
|
|
632
676
|
answer["droppedNodes"] = dropped["nodes"]
|
|
677
|
+
_note_left_out(answer, scene.written_pack, new_path, named, source)
|
|
633
678
|
if new_path != old_path:
|
|
634
679
|
answer.merge(GdhKin.follow(scene_path, old_path, new_path, carried))
|
|
635
680
|
return answer
|
|
636
681
|
|
|
637
682
|
|
|
683
|
+
## Notes under notStored each of [param named] the file saved from [param pack] does not set on the
|
|
684
|
+
## node at [param at]: a set gave it the value the node has without it, so the packer leaves it out,
|
|
685
|
+
## as the editor does, and it follows [param source], or its default, from then on.
|
|
686
|
+
static func _note_left_out(
|
|
687
|
+
answer: Dictionary, pack: PackedScene, at: String, named: Array[String], source: String
|
|
688
|
+
) -> void:
|
|
689
|
+
var stored: Dictionary = {}
|
|
690
|
+
var state: SceneState = pack.get_state()
|
|
691
|
+
for index: int in state.get_node_count():
|
|
692
|
+
if str(state.get_node_path(index)).trim_prefix("./") == at:
|
|
693
|
+
for property: int in state.get_node_property_count(index):
|
|
694
|
+
stored[str(state.get_node_property_name(index, property))] = true
|
|
695
|
+
var left: Array[String] = []
|
|
696
|
+
for asked: String in named:
|
|
697
|
+
if not stored.has(asked):
|
|
698
|
+
left.append(asked)
|
|
699
|
+
if left.is_empty():
|
|
700
|
+
return
|
|
701
|
+
answer["notStored"] = left
|
|
702
|
+
var names: String = ", ".join(left)
|
|
703
|
+
var verb: String = "holds" if left.size() == 1 else "hold"
|
|
704
|
+
var pronoun: String = "it" if left.size() == 1 else "them"
|
|
705
|
+
if source.is_empty():
|
|
706
|
+
answer["notStoredNote"] = (
|
|
707
|
+
"%s now %s the default, so the file does not store %s." % [names, verb, pronoun]
|
|
708
|
+
)
|
|
709
|
+
else:
|
|
710
|
+
answer["notStoredNote"] = (
|
|
711
|
+
(
|
|
712
|
+
"%s now %s what %s gives this node, so the file does not store %s, and a change there reaches "
|
|
713
|
+
+ "this node."
|
|
714
|
+
)
|
|
715
|
+
% [names, verb, source, pronoun]
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
|
|
638
719
|
func get_node_properties(args: Dictionary) -> Dictionary:
|
|
639
720
|
var project_path: String = str(args.get("projectPath", ""))
|
|
640
721
|
var scene_path: String = GdhSceneFile.res_path(project_path, str(args.get("scenePath", "")))
|
|
@@ -657,11 +738,55 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
657
738
|
# names this scene's file sets are always answered.
|
|
658
739
|
var set_here: Dictionary = scene.set_here(node)
|
|
659
740
|
var props: Dictionary = _properties_of(node, include_defaults, set_here)
|
|
660
|
-
var cells: Dictionary =
|
|
741
|
+
var cells: Dictionary = GdhTilesetTools.cells_of(node)
|
|
661
742
|
var at: String = scene.path_of(node)
|
|
662
743
|
var here: Array = set_here.keys()
|
|
663
744
|
here.sort()
|
|
664
745
|
var answer: Dictionary = {"ok": true, "nodePath": at, "properties": props, "setHere": here}
|
|
746
|
+
# A Node3D stores only its transform, a matrix, while a set takes the position, rotation and
|
|
747
|
+
# scale the inspector shows, so a node turned 45 degrees down read back as nine numbers. A
|
|
748
|
+
# sheared one has no such three, and is left to its transform.
|
|
749
|
+
var placed: Node3D = node as Node3D
|
|
750
|
+
var rebuilt: Transform3D = Transform3D()
|
|
751
|
+
if placed != null:
|
|
752
|
+
var turned: Basis = Basis.from_euler(placed.rotation, placed.rotation_order)
|
|
753
|
+
rebuilt = Transform3D(turned * Basis.from_scale(placed.scale), placed.position)
|
|
754
|
+
if placed != null and props.has("transform") and rebuilt.is_equal_approx(placed.transform):
|
|
755
|
+
# Rounded, since a node never scaled came back with a scale of 0.99999994 out of the matrix;
|
|
756
|
+
# the transform beside it is exact.
|
|
757
|
+
var step: Vector3 = Vector3.ONE * 0.00001
|
|
758
|
+
answer["placement"] = {
|
|
759
|
+
"position": _readable(node, placed.position.snapped(step)),
|
|
760
|
+
"rotation_degrees": _readable(node, placed.rotation_degrees.snapped(step)),
|
|
761
|
+
"scale": _readable(node, placed.scale.snapped(step)),
|
|
762
|
+
}
|
|
763
|
+
# A player's getter answers Master for a bus the layout does not have, while the file keeps the
|
|
764
|
+
# name, so a player left on a renamed or removed bus read as one put on Master.
|
|
765
|
+
var bus: String = str(set_here.get("bus", ""))
|
|
766
|
+
var plays: bool = node is AudioStreamPlayer or node is AudioStreamPlayer2D or node is AudioStreamPlayer3D
|
|
767
|
+
if plays and not bus.is_empty() and AudioServer.get_bus_index(bus) == -1:
|
|
768
|
+
props["bus"] = bus
|
|
769
|
+
var buses: PackedStringArray = []
|
|
770
|
+
for index: int in AudioServer.bus_count:
|
|
771
|
+
var _added: bool = buses.append(AudioServer.get_bus_name(index))
|
|
772
|
+
answer["busNote"] = (
|
|
773
|
+
(
|
|
774
|
+
"%s names the bus %s, which the project's bus layout does not have, so it plays on %s; "
|
|
775
|
+
+ "the buses are %s."
|
|
776
|
+
)
|
|
777
|
+
% [str(node.name) if at == "." else at, bus, AudioServer.get_bus_name(0), ", ".join(buses)]
|
|
778
|
+
)
|
|
779
|
+
# A mask reads as a number, and which layers it holds, under the names the project gives them,
|
|
780
|
+
# is what a caller reads it for.
|
|
781
|
+
var layers: Dictionary = {}
|
|
782
|
+
for declared: Dictionary in node.get_property_list():
|
|
783
|
+
var hint: int = GdhRead.as_int(declared.get("hint", PROPERTY_HINT_NONE))
|
|
784
|
+
var named: String = str(declared.get("name", ""))
|
|
785
|
+
var mask: Variant = node.get(named) if props.has(named) else null
|
|
786
|
+
if hint in GdhDeclared.LAYER_NAMES and mask is int:
|
|
787
|
+
layers[named] = GdhDeclared.layers_in(hint, GdhRead.as_int(mask))
|
|
788
|
+
if not layers.is_empty():
|
|
789
|
+
answer["layers"] = layers
|
|
665
790
|
answer.merge(GdhSceneFile.class_of(node))
|
|
666
791
|
answer.merge(GdhGroups.read(scene, node))
|
|
667
792
|
var from: String = scene.values_from(node, at)
|
|
@@ -684,6 +809,24 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
684
809
|
return answer
|
|
685
810
|
|
|
686
811
|
|
|
812
|
+
func bake_navigation(args: Dictionary) -> Dictionary:
|
|
813
|
+
var project_path: String = str(args.get("projectPath", ""))
|
|
814
|
+
var scene_path: String = GdhSceneFile.res_path(project_path, str(args.get("scenePath", "")))
|
|
815
|
+
var node_path: String = str(args.get("nodePath", "."))
|
|
816
|
+
var opened: Array = _open(scene_path, true)
|
|
817
|
+
var scene: GdhSceneFile = opened[0]
|
|
818
|
+
var refused: Dictionary = opened[1]
|
|
819
|
+
if not refused.is_empty():
|
|
820
|
+
return refused
|
|
821
|
+
var node: Node = GdhSceneFile.node_at(scene.root, node_path)
|
|
822
|
+
if not node:
|
|
823
|
+
return scene.refuse(scene.not_found("Node not found: " + node_path, node_path))
|
|
824
|
+
var foreign: String = scene.foreign(node)
|
|
825
|
+
if not foreign.is_empty():
|
|
826
|
+
return scene.refuse(foreign)
|
|
827
|
+
return GdhNavigationBake.bake(scene, node)
|
|
828
|
+
|
|
829
|
+
|
|
687
830
|
func save_scene(args: Dictionary) -> Dictionary:
|
|
688
831
|
var project_path: String = str(args.get("projectPath", ""))
|
|
689
832
|
var scene_path: String = GdhSceneFile.res_path(project_path, str(args.get("scenePath", "")))
|
|
@@ -10,12 +10,12 @@ extends Node
|
|
|
10
10
|
## of the two a caller wants is theirs to say, so the server only comes here when asked, and the
|
|
11
11
|
## difference is the reason the argument exists rather than an inconvenience to paper over.
|
|
12
12
|
##
|
|
13
|
-
## Every
|
|
13
|
+
## Every answer is built by the same reads the headless answer uses, because the two are one
|
|
14
14
|
## question asked of two places: a value spelled differently here would read as the editor and the
|
|
15
15
|
## file disagreeing about something they agree about.
|
|
16
16
|
|
|
17
|
-
const GdhRead = preload("../reading.gd")
|
|
18
17
|
const GdhSerialisation = preload("../serialisation.gd")
|
|
18
|
+
const GdhSettingReads = preload("../setting_reads.gd")
|
|
19
19
|
|
|
20
20
|
var _values: GdhSerialisation = GdhSerialisation.new()
|
|
21
21
|
|
|
@@ -23,42 +23,19 @@ var _values: GdhSerialisation = GdhSerialisation.new()
|
|
|
23
23
|
func get_project_setting(args: Dictionary) -> Dictionary:
|
|
24
24
|
var prefix: String = str(args.get("prefix", ""))
|
|
25
25
|
if not prefix.is_empty():
|
|
26
|
-
|
|
26
|
+
var family: Dictionary = GdhSettingReads.under(prefix, _values)
|
|
27
|
+
family["ok"] = true
|
|
28
|
+
return family
|
|
27
29
|
|
|
28
30
|
var setting: String = str(args.get("setting", ""))
|
|
29
31
|
if setting.is_empty():
|
|
30
32
|
return {"ok": false, "error": "setting is required, or prefix to read every setting under one"}
|
|
31
33
|
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
-
if exists:
|
|
35
|
-
result["value"] = _values.serialize_value(ProjectSettings.get_setting(setting))
|
|
36
|
-
else:
|
|
37
|
-
result["message"] = "Setting does not exist"
|
|
34
|
+
var result: Dictionary = GdhSettingReads.one(setting, _values)
|
|
35
|
+
result["ok"] = true
|
|
38
36
|
return result
|
|
39
37
|
|
|
40
38
|
|
|
41
|
-
## Every setting under [prefix] with the type the engine registers for each, which is the half a
|
|
42
|
-
## name hides: a family of levels can hold a setting that is a bool, and writing a level over it
|
|
43
|
-
## looks like it worked.
|
|
44
|
-
func _settings_under(prefix: String) -> Dictionary:
|
|
45
|
-
var found: Array[Dictionary] = []
|
|
46
|
-
for property: Dictionary in ProjectSettings.get_property_list():
|
|
47
|
-
var setting: String = str(property.get("name", ""))
|
|
48
|
-
if not setting.begins_with(prefix):
|
|
49
|
-
continue
|
|
50
|
-
var kind: int = GdhRead.as_int(property.get("type", TYPE_NIL), TYPE_NIL)
|
|
51
|
-
var described: Dictionary = {
|
|
52
|
-
"setting": setting,
|
|
53
|
-
"type": type_string(kind),
|
|
54
|
-
"value": _values.serialize_value(ProjectSettings.get_setting(setting)),
|
|
55
|
-
}
|
|
56
|
-
found.append(described)
|
|
57
|
-
|
|
58
|
-
found.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return a["setting"] < b["setting"])
|
|
59
|
-
return {"ok": true, "prefix": prefix, "count": found.size(), "settings": found}
|
|
60
|
-
|
|
61
|
-
|
|
62
39
|
## Takes the named settings from project.godot as it is on disk, after something else wrote it.
|
|
63
40
|
##
|
|
64
41
|
## The editor loads the file once, at startup. Written from outside, it goes on holding the old
|