gdharness 1.3.0 → 1.3.1
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 +14 -13
- package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
- package/build/godot/addons/gdharness_editor/bridge_client.gd +8 -8
- package/build/godot/addons/gdharness_editor/file_reads.gd +60 -25
- package/build/godot/addons/gdharness_editor/headless_check.gd +7 -7
- package/build/godot/addons/gdharness_editor/plugin.gd +6 -6
- package/build/godot/addons/gdharness_editor/property_values.gd +120 -53
- package/build/godot/addons/gdharness_editor/reading.gd +28 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
- package/build/godot/addons/gdharness_editor/serialisation.gd +45 -94
- package/build/godot/addons/gdharness_editor/tool_executor.gd +24 -24
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +52 -41
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +14 -14
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +48 -41
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +17 -17
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +14 -12
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +7 -7
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +81 -77
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/value_shapes.gd +111 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +28 -0
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +29 -29
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +21 -18
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -64
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +8 -8
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -10
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +7 -7
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +41 -41
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +33 -33
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +20 -20
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +25 -24
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
- package/build/godot/addons/gdharness_runtime/serialisation.gd +45 -94
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +111 -0
- package/build/godot/addons/gdharness_runtime/value_shapes.gd.uid +1 -0
- package/build/godot/operations/animation_reads.gd +7 -19
- package/build/godot/operations/audio_buses.gd +10 -10
- package/build/godot/operations/class_cache.gd +16 -16
- package/build/godot/operations/classdb_queries.gd +12 -12
- package/build/godot/operations/dependencies.gd +33 -29
- package/build/godot/operations/editor_tools.gd +12 -12
- package/build/godot/operations/export_presets.gd +10 -8
- package/build/godot/operations/file_reads.gd +60 -25
- package/build/godot/operations/gdscript_analysis.gd +20 -20
- package/build/godot/operations/gdscript_authoring.gd +17 -15
- package/build/godot/operations/godot_operations.gd +64 -63
- package/build/godot/operations/import_pipeline.gd +20 -20
- package/build/godot/operations/input_actions.gd +17 -17
- package/build/godot/operations/logger.gd +1 -1
- package/build/godot/operations/plugins.gd +8 -8
- package/build/godot/operations/project_config.gd +24 -17
- package/build/godot/operations/project_diagnostics.gd +9 -9
- package/build/godot/operations/property_values.gd +120 -53
- package/build/godot/operations/reading.gd +28 -0
- package/build/godot/operations/rename_checks.gd +3 -3
- package/build/godot/operations/resource_files.gd +5 -5
- package/build/godot/operations/script_check.gd +7 -7
- package/build/godot/operations/serialisation.gd +45 -94
- package/build/godot/operations/value_shapes.gd +111 -0
- package/package.json +1 -1
|
@@ -8,7 +8,8 @@ extends RefCounted
|
|
|
8
8
|
## before: the same Vector2 came back three ways depending on which tool was asked, and a caller
|
|
9
9
|
## reading a property off the editor could not feed it back to the game.
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const GdhRead = preload("reading.gd")
|
|
12
|
+
const GdhShapes = preload("value_shapes.gd")
|
|
12
13
|
|
|
13
14
|
# What the engine carries from one to another without complaint, which is what a value written to a
|
|
14
15
|
# property or handed to a parameter may arrive as. Numbers include bool because the engine counts it
|
|
@@ -120,35 +121,6 @@ const BUILDERS: Dictionary = {
|
|
|
120
121
|
"Dictionary": "_build_dictionary",
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
# What each tag has to carry before it is built. A tag naming a type whose keys are not there is a
|
|
124
|
-
# tag somebody wrote by mistake or wrote for a different shape, and building one out of defaults
|
|
125
|
-
# answers with a Transform2D of zeros where the caller meant something else entirely. Unbuilt, it
|
|
126
|
-
# comes back as it arrived, and whoever asked can read it against the type they were expecting.
|
|
127
|
-
const REQUIRED: Dictionary = {
|
|
128
|
-
"Vector2": [["x", "y"]],
|
|
129
|
-
"Vector2i": [["x", "y"]],
|
|
130
|
-
"Vector3": [["x", "y", "z"]],
|
|
131
|
-
"Vector3i": [["x", "y", "z"]],
|
|
132
|
-
"Vector4": [["x", "y", "z", "w"]],
|
|
133
|
-
"Vector4i": [["x", "y", "z", "w"]],
|
|
134
|
-
"Rect2": [["position", "size"], ["x", "y", "width", "height"]],
|
|
135
|
-
"Rect2i": [["position", "size"], ["x", "y", "width", "height"]],
|
|
136
|
-
"Plane": [["normal", "d"]],
|
|
137
|
-
"Quaternion": [["x", "y", "z", "w"]],
|
|
138
|
-
"AABB": [["position", "size"]],
|
|
139
|
-
"Basis": [["x", "y", "z"]],
|
|
140
|
-
"Transform2D": [["x", "y", "origin"]],
|
|
141
|
-
"Transform3D": [["basis", "origin"]],
|
|
142
|
-
"Projection": [["x", "y", "z", "w"]],
|
|
143
|
-
"Color": [["r", "g", "b"]],
|
|
144
|
-
"NodePath": [["path"]],
|
|
145
|
-
"PackedByteArray": [["base64"]],
|
|
146
|
-
"float": [["value"]],
|
|
147
|
-
"int": [["value"]],
|
|
148
|
-
"RID": [["id"]],
|
|
149
|
-
"Dictionary": [["entries"]],
|
|
150
|
-
}
|
|
151
|
-
|
|
152
124
|
|
|
153
125
|
# Converts a Godot value into something JSON can carry. A type with no entry in the table
|
|
154
126
|
# passes through as itself, which is what the JSON-native ones want.
|
|
@@ -246,8 +218,8 @@ func deserialize_value(value: Variant) -> Variant:
|
|
|
246
218
|
# under the target project's warning levels, so one such project lost every headless call.
|
|
247
219
|
var tag: String = str(fields["_type"])
|
|
248
220
|
var builder: String = BUILDERS.get(tag, "")
|
|
249
|
-
var
|
|
250
|
-
if builder.is_empty() or not
|
|
221
|
+
var spare: Array = GdhShapes.spare(fields, tag)
|
|
222
|
+
if builder.is_empty() or not spare.any(func(left: Array) -> bool: return left.is_empty()):
|
|
251
223
|
return fields
|
|
252
224
|
if tag == "float" and str(fields["value"]) not in ["inf", "-inf", "nan"]:
|
|
253
225
|
return fields
|
|
@@ -430,6 +402,11 @@ func fitted(value: Variant, type: int) -> Variant:
|
|
|
430
402
|
if rebuilt is Array and LIST_TYPES.has(type):
|
|
431
403
|
return type_convert(rebuilt, type)
|
|
432
404
|
|
|
405
|
+
# The inspector and every colour picker give a colour as its html code. Anything else stays the
|
|
406
|
+
# text it is, for the refusal to name.
|
|
407
|
+
if type == TYPE_COLOR and rebuilt is String and Color.html_is_valid(str(rebuilt)):
|
|
408
|
+
return Color.html(str(rebuilt))
|
|
409
|
+
|
|
433
410
|
var simple: Array[int] = [TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING]
|
|
434
411
|
if not simple.has(type) or not simple.has(typeof(rebuilt)):
|
|
435
412
|
return rebuilt
|
|
@@ -441,7 +418,7 @@ func fitted(value: Variant, type: int) -> Variant:
|
|
|
441
418
|
|
|
442
419
|
if rebuilt is String and type != TYPE_STRING:
|
|
443
420
|
var text: String = rebuilt
|
|
444
|
-
var parsed: Variant =
|
|
421
|
+
var parsed: Variant = GdhRead.json_or_null(text)
|
|
445
422
|
# A string that does not read as the type wanted is handed back as the string it is, so
|
|
446
423
|
# whoever asked can refuse it. type_convert answers 0 for "not an index" and true for any
|
|
447
424
|
# text at all, and a caller who sent a word got back a number they never sent: get_child
|
|
@@ -461,7 +438,7 @@ func fitted(value: Variant, type: int) -> Variant:
|
|
|
461
438
|
static func inexact(given: Variant, type: int) -> String:
|
|
462
439
|
if type != TYPE_INT or (typeof(given) != TYPE_FLOAT and typeof(given) != TYPE_INT):
|
|
463
440
|
return ""
|
|
464
|
-
var number: float =
|
|
441
|
+
var number: float = GdhRead.as_float(given)
|
|
465
442
|
if not is_finite(number) or absf(number) < EXACT:
|
|
466
443
|
return ""
|
|
467
444
|
return (
|
|
@@ -474,34 +451,6 @@ static func inexact(given: Variant, type: int) -> String:
|
|
|
474
451
|
)
|
|
475
452
|
|
|
476
453
|
|
|
477
|
-
## How a value of [param type] is written to be read as one, for a refusal to name, or empty for a
|
|
478
|
-
## type that is not built from fields. A refusal naming only the two types left a caller to find
|
|
479
|
-
## the shape in the description of another op.
|
|
480
|
-
static func written_as(type: int) -> String:
|
|
481
|
-
var tag: String = type_string(type)
|
|
482
|
-
var alternatives: Array = REQUIRED.get(tag, [])
|
|
483
|
-
if alternatives.is_empty() or tag in ["float", "int", "Dictionary"]:
|
|
484
|
-
return ""
|
|
485
|
-
var keys: Array = alternatives[0]
|
|
486
|
-
var parts: Array[String] = []
|
|
487
|
-
for key: Variant in keys:
|
|
488
|
-
parts.append('"%s": ...' % key)
|
|
489
|
-
return "{%s}" % ", ".join(parts)
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
## Whether [param fields] carries every key of any one of [param alternatives].
|
|
493
|
-
##
|
|
494
|
-
## More than one spelling because a rectangle has two: the two corners it is answered with, and the
|
|
495
|
-
## four numbers the editor's own tools answered with before the serialisers were made one, which a
|
|
496
|
-
## caller may have kept.
|
|
497
|
-
func _carries(fields: Dictionary, alternatives: Array) -> bool:
|
|
498
|
-
for alternative: Variant in alternatives:
|
|
499
|
-
var keys: Array = alternative
|
|
500
|
-
if keys.all(func(key: Variant) -> bool: return fields.has(key)):
|
|
501
|
-
return true
|
|
502
|
-
return false
|
|
503
|
-
|
|
504
|
-
|
|
505
454
|
func _serialize_nil(_value: Variant) -> Variant:
|
|
506
455
|
return null
|
|
507
456
|
|
|
@@ -769,42 +718,44 @@ func _describe_object(value: Object) -> Dictionary:
|
|
|
769
718
|
|
|
770
719
|
|
|
771
720
|
func _build_vector2(fields: Dictionary) -> Vector2:
|
|
772
|
-
return Vector2(
|
|
721
|
+
return Vector2(GdhRead.as_float(fields.get("x", 0)), GdhRead.as_float(fields.get("y", 0)))
|
|
773
722
|
|
|
774
723
|
|
|
775
724
|
func _build_vector2i(fields: Dictionary) -> Vector2i:
|
|
776
|
-
return Vector2i(
|
|
725
|
+
return Vector2i(GdhRead.as_int(fields.get("x", 0)), GdhRead.as_int(fields.get("y", 0)))
|
|
777
726
|
|
|
778
727
|
|
|
779
728
|
func _build_vector3(fields: Dictionary) -> Vector3:
|
|
780
729
|
return Vector3(
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
730
|
+
GdhRead.as_float(fields.get("x", 0)),
|
|
731
|
+
GdhRead.as_float(fields.get("y", 0)),
|
|
732
|
+
GdhRead.as_float(fields.get("z", 0))
|
|
784
733
|
)
|
|
785
734
|
|
|
786
735
|
|
|
787
736
|
func _build_vector3i(fields: Dictionary) -> Vector3i:
|
|
788
737
|
return Vector3i(
|
|
789
|
-
|
|
738
|
+
GdhRead.as_int(fields.get("x", 0)),
|
|
739
|
+
GdhRead.as_int(fields.get("y", 0)),
|
|
740
|
+
GdhRead.as_int(fields.get("z", 0))
|
|
790
741
|
)
|
|
791
742
|
|
|
792
743
|
|
|
793
744
|
func _build_vector4(fields: Dictionary) -> Vector4:
|
|
794
745
|
return Vector4(
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
746
|
+
GdhRead.as_float(fields.get("x", 0)),
|
|
747
|
+
GdhRead.as_float(fields.get("y", 0)),
|
|
748
|
+
GdhRead.as_float(fields.get("z", 0)),
|
|
749
|
+
GdhRead.as_float(fields.get("w", 0))
|
|
799
750
|
)
|
|
800
751
|
|
|
801
752
|
|
|
802
753
|
func _build_vector4i(fields: Dictionary) -> Vector4i:
|
|
803
754
|
return Vector4i(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
755
|
+
GdhRead.as_int(fields.get("x", 0)),
|
|
756
|
+
GdhRead.as_int(fields.get("y", 0)),
|
|
757
|
+
GdhRead.as_int(fields.get("z", 0)),
|
|
758
|
+
GdhRead.as_int(fields.get("w", 0))
|
|
808
759
|
)
|
|
809
760
|
|
|
810
761
|
|
|
@@ -813,10 +764,10 @@ func _build_vector4i(fields: Dictionary) -> Vector4i:
|
|
|
813
764
|
func _build_rect2(fields: Dictionary) -> Rect2:
|
|
814
765
|
if not fields.has("position"):
|
|
815
766
|
return Rect2(
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
767
|
+
GdhRead.as_float(fields.get("x", 0)),
|
|
768
|
+
GdhRead.as_float(fields.get("y", 0)),
|
|
769
|
+
GdhRead.as_float(fields.get("width", 0)),
|
|
770
|
+
GdhRead.as_float(fields.get("height", 0))
|
|
820
771
|
)
|
|
821
772
|
return Rect2(_vector2_at(fields, "position"), _vector2_at(fields, "size"))
|
|
822
773
|
|
|
@@ -824,24 +775,24 @@ func _build_rect2(fields: Dictionary) -> Rect2:
|
|
|
824
775
|
func _build_rect2i(fields: Dictionary) -> Rect2i:
|
|
825
776
|
if not fields.has("position"):
|
|
826
777
|
return Rect2i(
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
778
|
+
GdhRead.as_int(fields.get("x", 0)),
|
|
779
|
+
GdhRead.as_int(fields.get("y", 0)),
|
|
780
|
+
GdhRead.as_int(fields.get("width", 0)),
|
|
781
|
+
GdhRead.as_int(fields.get("height", 0))
|
|
831
782
|
)
|
|
832
783
|
return Rect2i(_vector2i_at(fields, "position"), _vector2i_at(fields, "size"))
|
|
833
784
|
|
|
834
785
|
|
|
835
786
|
func _build_plane(fields: Dictionary) -> Plane:
|
|
836
|
-
return Plane(_vector3_at(fields, "normal"),
|
|
787
|
+
return Plane(_vector3_at(fields, "normal"), GdhRead.as_float(fields.get("d", 0)))
|
|
837
788
|
|
|
838
789
|
|
|
839
790
|
func _build_quaternion(fields: Dictionary) -> Quaternion:
|
|
840
791
|
return Quaternion(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
792
|
+
GdhRead.as_float(fields.get("x", 0)),
|
|
793
|
+
GdhRead.as_float(fields.get("y", 0)),
|
|
794
|
+
GdhRead.as_float(fields.get("z", 0)),
|
|
795
|
+
GdhRead.as_float(fields.get("w", 0))
|
|
845
796
|
)
|
|
846
797
|
|
|
847
798
|
|
|
@@ -881,10 +832,10 @@ func _build_projection(fields: Dictionary) -> Projection:
|
|
|
881
832
|
|
|
882
833
|
func _build_color(fields: Dictionary) -> Color:
|
|
883
834
|
return Color(
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
835
|
+
GdhRead.as_float(fields.get("r", 0)),
|
|
836
|
+
GdhRead.as_float(fields.get("g", 0)),
|
|
837
|
+
GdhRead.as_float(fields.get("b", 0)),
|
|
838
|
+
GdhRead.as_float(fields.get("a", 1), 1.0)
|
|
888
839
|
)
|
|
889
840
|
|
|
890
841
|
|
|
@@ -897,7 +848,7 @@ func _build_bytes(fields: Dictionary) -> PackedByteArray:
|
|
|
897
848
|
|
|
898
849
|
|
|
899
850
|
func _build_float(fields: Dictionary) -> float:
|
|
900
|
-
return
|
|
851
|
+
return GdhRead.as_float(fields)
|
|
901
852
|
|
|
902
853
|
|
|
903
854
|
func _build_int(fields: Dictionary) -> int:
|
|
@@ -5,25 +5,25 @@ extends Node
|
|
|
5
5
|
## preloaded rather than looked up on disk at runtime, so a missing one fails to parse here
|
|
6
6
|
## instead of leaving a command silently unanswered.
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
8
|
+
const GdhSceneTools = preload("tools/scene_tools.gd")
|
|
9
|
+
const GdhResourceTools = preload("tools/resource_tools.gd")
|
|
10
|
+
const GdhAnimationTools = preload("tools/animation_tools.gd")
|
|
11
|
+
const GdhPlayTools = preload("tools/play_tools.gd")
|
|
12
|
+
const GdhClassTools = preload("tools/class_tools.gd")
|
|
13
|
+
const GdhSettingsTools = preload("tools/settings_tools.gd")
|
|
14
|
+
const GdhFilesystemTools = preload("tools/filesystem_tools.gd")
|
|
15
|
+
const GdhProfileTools = preload("tools/profile_tools.gd")
|
|
16
16
|
|
|
17
17
|
var _editor_plugin: EditorPlugin = null
|
|
18
18
|
|
|
19
|
-
var _scene_tools:
|
|
20
|
-
var _resource_tools:
|
|
21
|
-
var _animation_tools:
|
|
22
|
-
var _play_tools:
|
|
23
|
-
var _class_tools:
|
|
24
|
-
var _settings_tools:
|
|
25
|
-
var _filesystem_tools:
|
|
26
|
-
var _profile_tools:
|
|
19
|
+
var _scene_tools: GdhSceneTools = null
|
|
20
|
+
var _resource_tools: GdhResourceTools = null
|
|
21
|
+
var _animation_tools: GdhAnimationTools = null
|
|
22
|
+
var _play_tools: GdhPlayTools = null
|
|
23
|
+
var _class_tools: GdhClassTools = null
|
|
24
|
+
var _settings_tools: GdhSettingsTools = null
|
|
25
|
+
var _filesystem_tools: GdhFilesystemTools = null
|
|
26
|
+
var _profile_tools: GdhProfileTools = null
|
|
27
27
|
|
|
28
28
|
var _tool_map: Dictionary = {}
|
|
29
29
|
var _initialized: bool = false
|
|
@@ -46,35 +46,35 @@ func _init_tools() -> void:
|
|
|
46
46
|
return
|
|
47
47
|
_initialized = true
|
|
48
48
|
|
|
49
|
-
_scene_tools =
|
|
49
|
+
_scene_tools = GdhSceneTools.new()
|
|
50
50
|
_scene_tools.name = "SceneTools"
|
|
51
51
|
add_child(_scene_tools)
|
|
52
52
|
|
|
53
|
-
_resource_tools =
|
|
53
|
+
_resource_tools = GdhResourceTools.new()
|
|
54
54
|
_resource_tools.name = "ResourceTools"
|
|
55
55
|
add_child(_resource_tools)
|
|
56
56
|
|
|
57
|
-
_animation_tools =
|
|
57
|
+
_animation_tools = GdhAnimationTools.new()
|
|
58
58
|
_animation_tools.name = "AnimationTools"
|
|
59
59
|
add_child(_animation_tools)
|
|
60
60
|
|
|
61
|
-
_play_tools =
|
|
61
|
+
_play_tools = GdhPlayTools.new()
|
|
62
62
|
_play_tools.name = "PlayTools"
|
|
63
63
|
add_child(_play_tools)
|
|
64
64
|
|
|
65
|
-
_class_tools =
|
|
65
|
+
_class_tools = GdhClassTools.new()
|
|
66
66
|
_class_tools.name = "ClassTools"
|
|
67
67
|
add_child(_class_tools)
|
|
68
68
|
|
|
69
|
-
_settings_tools =
|
|
69
|
+
_settings_tools = GdhSettingsTools.new()
|
|
70
70
|
_settings_tools.name = "SettingsTools"
|
|
71
71
|
add_child(_settings_tools)
|
|
72
72
|
|
|
73
|
-
_filesystem_tools =
|
|
73
|
+
_filesystem_tools = GdhFilesystemTools.new()
|
|
74
74
|
_filesystem_tools.name = "FilesystemTools"
|
|
75
75
|
add_child(_filesystem_tools)
|
|
76
76
|
|
|
77
|
-
_profile_tools =
|
|
77
|
+
_profile_tools = GdhProfileTools.new()
|
|
78
78
|
_profile_tools.name = "ProfileTools"
|
|
79
79
|
add_child(_profile_tools)
|
|
80
80
|
|
|
@@ -4,19 +4,19 @@ extends Node
|
|
|
4
4
|
## Animations and animation trees, edited in the open editor or, for a project no editor serves, by a
|
|
5
5
|
## headless engine.
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
7
|
+
const GdhAnimationReads = preload("../animation_reads.gd")
|
|
8
|
+
const GdhHeadlessCheck = preload("../headless_check.gd")
|
|
9
|
+
const GdhPropertyValues = preload("../property_values.gd")
|
|
10
|
+
const GdhRead = preload("../reading.gd")
|
|
11
|
+
const GdhSceneFile = preload("../scene_file.gd")
|
|
12
|
+
const GdhSerialisation = preload("../serialisation.gd")
|
|
13
13
|
|
|
14
14
|
## How far right of the rightmost state a new one goes in the editor's graph, wider than a state's
|
|
15
15
|
## box there.
|
|
16
16
|
const STATE_SPACING: float = 200.0
|
|
17
17
|
|
|
18
18
|
var _editor_plugin: EditorPlugin = null
|
|
19
|
-
var _properties:
|
|
19
|
+
var _properties: GdhPropertyValues = GdhPropertyValues.new()
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
@@ -42,15 +42,13 @@ func _find_node(root: Node, path: String) -> Node:
|
|
|
42
42
|
## [param kind] the scene owns. Answers [scene, node, refusal]; the refusal is {} when both were
|
|
43
43
|
## found.
|
|
44
44
|
func _open_at(scene_path: String, node_path: String, kind: String) -> Array:
|
|
45
|
-
var scene:
|
|
45
|
+
var scene: GdhSceneFile = GdhSceneFile.new()
|
|
46
46
|
var refused: Dictionary = scene.open(scene_path, true)
|
|
47
47
|
if not refused.is_empty():
|
|
48
48
|
return [scene, null, refused]
|
|
49
49
|
var node: Node = _find_node(scene.root, node_path)
|
|
50
50
|
if node == null or not node.is_class(kind):
|
|
51
|
-
return [
|
|
52
|
-
scene, null, scene.refuse(scene.not_found("%s not found at: %s" % [kind, node_path], node_path))
|
|
53
|
-
]
|
|
51
|
+
return [scene, null, _not_of_kind(scene, node, node_path, kind)]
|
|
54
52
|
var foreign: String = scene.foreign(node)
|
|
55
53
|
if not foreign.is_empty():
|
|
56
54
|
return [scene, null, scene.refuse(foreign)]
|
|
@@ -60,21 +58,34 @@ func _open_at(scene_path: String, node_path: String, kind: String) -> Array:
|
|
|
60
58
|
## [param scene_path] opened for reading, as [method _open_at] answers. A node an instanced scene
|
|
61
59
|
## owns is read like any other, since nothing is written.
|
|
62
60
|
func _read_at(scene_path: String, node_path: String, kind: String) -> Array:
|
|
63
|
-
var scene:
|
|
61
|
+
var scene: GdhSceneFile = GdhSceneFile.new()
|
|
64
62
|
var refused: Dictionary = scene.open(scene_path, false)
|
|
65
63
|
if not refused.is_empty():
|
|
66
64
|
return [scene, null, refused]
|
|
67
65
|
var node: Node = _find_node(scene.root, node_path)
|
|
68
66
|
if node == null or not node.is_class(kind):
|
|
69
|
-
return [
|
|
70
|
-
scene, null, scene.refuse(scene.not_found("%s not found at: %s" % [kind, node_path], node_path))
|
|
71
|
-
]
|
|
67
|
+
return [scene, null, _not_of_kind(scene, node, node_path, kind)]
|
|
72
68
|
return [scene, node, {}]
|
|
73
69
|
|
|
74
70
|
|
|
71
|
+
## The refusal for [param node_path] in [param scene], where [param node] is what the scene has
|
|
72
|
+
## there, null or not a [param kind], with where the scene's nodes of that kind are.
|
|
73
|
+
func _not_of_kind(scene: GdhSceneFile, node: Node, node_path: String, kind: String) -> Dictionary:
|
|
74
|
+
var said: String = "%s not found at: %s" % [kind, node_path]
|
|
75
|
+
if node == null:
|
|
76
|
+
said = scene.not_found(said, node_path)
|
|
77
|
+
var held: PackedStringArray = []
|
|
78
|
+
if scene.root.is_class(kind):
|
|
79
|
+
var _root_held: bool = held.append(".")
|
|
80
|
+
for found: Node in scene.root.find_children("*", kind, true, false):
|
|
81
|
+
var _held: bool = held.append(str(scene.root.get_path_to(found)))
|
|
82
|
+
var there: String = "" if node == null else node.get_class()
|
|
83
|
+
return scene.refuse(GdhRead.not_of_kind(said, node_path, there, kind, held))
|
|
84
|
+
|
|
85
|
+
|
|
75
86
|
## Writes [param scene] once [param check] passes, then the files of [param homes], answering
|
|
76
87
|
## [param answer] or a refusal.
|
|
77
|
-
func _finish(scene:
|
|
88
|
+
func _finish(scene: GdhSceneFile, check: Callable, homes: Dictionary, answer: Dictionary) -> Dictionary:
|
|
78
89
|
if not Engine.is_editor_hint():
|
|
79
90
|
for file: Variant in homes:
|
|
80
91
|
var home: Resource = homes[file]
|
|
@@ -91,7 +102,7 @@ func _finish(scene: SceneFile, check: Callable, homes: Dictionary, answer: Dicti
|
|
|
91
102
|
)
|
|
92
103
|
% file
|
|
93
104
|
)
|
|
94
|
-
+
|
|
105
|
+
+ GdhHeadlessCheck.REMEDY
|
|
95
106
|
)
|
|
96
107
|
)
|
|
97
108
|
)
|
|
@@ -100,7 +111,7 @@ func _finish(scene: SceneFile, check: Callable, homes: Dictionary, answer: Dicti
|
|
|
100
111
|
return written
|
|
101
112
|
for file: Variant in homes:
|
|
102
113
|
var resource: Resource = homes[file]
|
|
103
|
-
var saving: Error =
|
|
114
|
+
var saving: Error = GdhSceneFile.save_keeping_uid(resource, str(file))
|
|
104
115
|
if saving != OK:
|
|
105
116
|
return {"ok": false, "error": "Failed to save %s: %s" % [file, error_string(saving)]}
|
|
106
117
|
if Engine.is_editor_hint():
|
|
@@ -112,14 +123,14 @@ func _finish(scene: SceneFile, check: Callable, homes: Dictionary, answer: Dicti
|
|
|
112
123
|
## engine ran as it loaded the file.
|
|
113
124
|
func _scripted(resource: Resource) -> bool:
|
|
114
125
|
var script: Script = resource.get_script()
|
|
115
|
-
return (script != null and not script.is_tool()) or
|
|
126
|
+
return (script != null and not script.is_tool()) or GdhHeadlessCheck.holds_scripted_embedded(resource, {})
|
|
116
127
|
|
|
117
128
|
|
|
118
129
|
func _parse_json_maybe(value: Variant) -> Variant:
|
|
119
130
|
if typeof(value) != TYPE_STRING:
|
|
120
131
|
return value
|
|
121
132
|
var text: String = value
|
|
122
|
-
var parsed: Variant =
|
|
133
|
+
var parsed: Variant = GdhRead.json_or_null(text)
|
|
123
134
|
if parsed == null and text != "null":
|
|
124
135
|
return value
|
|
125
136
|
return parsed
|
|
@@ -140,7 +151,7 @@ func _parse_method_args(raw_args: Array) -> Array:
|
|
|
140
151
|
## edited in memory while only the scene was saved, which refers to the file, so the edit was
|
|
141
152
|
## answered as made and was gone; it is saved too. One inside some other file (an imported model,
|
|
142
153
|
## the scene this one inherits from) is refused, since nothing written here would keep the edit.
|
|
143
|
-
func _homes(scene:
|
|
154
|
+
func _homes(scene: GdhSceneFile, held: Array) -> Array:
|
|
144
155
|
var homes: Dictionary = {}
|
|
145
156
|
for entry: Array in held:
|
|
146
157
|
var resource: Resource = entry[0]
|
|
@@ -195,10 +206,10 @@ func _library_of(player: AnimationPlayer, animation_name: String) -> AnimationLi
|
|
|
195
206
|
|
|
196
207
|
## The state machine at [param state_machine_path] in [param anim_tree], with the files it is kept
|
|
197
208
|
## in, as [machine, homes, why not].
|
|
198
|
-
func _machine(scene:
|
|
199
|
-
var sm: AnimationNodeStateMachine =
|
|
209
|
+
func _machine(scene: GdhSceneFile, anim_tree: AnimationTree, state_machine_path: String) -> Array:
|
|
210
|
+
var sm: AnimationNodeStateMachine = GdhAnimationReads.state_machine(anim_tree, state_machine_path)
|
|
200
211
|
if not sm:
|
|
201
|
-
return [null, {},
|
|
212
|
+
return [null, {}, GdhAnimationReads.no_machine(anim_tree, state_machine_path)]
|
|
202
213
|
var homes: Array = _homes(scene, [[sm, "state machine"]])
|
|
203
214
|
if not homes[0]:
|
|
204
215
|
return [null, {}, homes[1]]
|
|
@@ -226,18 +237,18 @@ func read_animations(args: Dictionary) -> Dictionary:
|
|
|
226
237
|
return {"ok": false, "error": "Missing scenePath"}
|
|
227
238
|
|
|
228
239
|
var opened: Array = _read_at(scene_path, player_node_path, "AnimationPlayer")
|
|
229
|
-
var scene:
|
|
240
|
+
var scene: GdhSceneFile = opened[0]
|
|
230
241
|
if opened[1] == null:
|
|
231
242
|
return opened[2]
|
|
232
243
|
var player: AnimationPlayer = opened[1]
|
|
233
|
-
return _read_in(scene,
|
|
244
|
+
return _read_in(scene, GdhAnimationReads.animations(player, animation_name))
|
|
234
245
|
|
|
235
246
|
|
|
236
247
|
## [param answer] read from [param scene], with whether the editor holds the scene unsaved.
|
|
237
|
-
func _read_in(scene:
|
|
248
|
+
func _read_in(scene: GdhSceneFile, answer: Dictionary) -> Dictionary:
|
|
238
249
|
if not answer.get("ok", false):
|
|
239
250
|
return scene.refuse(str(answer.get("error", "")))
|
|
240
|
-
answer["unsavedInEditor"] =
|
|
251
|
+
answer["unsavedInEditor"] = GdhSceneFile.unsaved(scene.path)
|
|
241
252
|
scene.close()
|
|
242
253
|
return answer
|
|
243
254
|
|
|
@@ -253,11 +264,11 @@ func read_animation_states(args: Dictionary) -> Dictionary:
|
|
|
253
264
|
return {"ok": false, "error": "Missing animTreePath"}
|
|
254
265
|
|
|
255
266
|
var opened: Array = _read_at(scene_path, anim_tree_path, "AnimationTree")
|
|
256
|
-
var scene:
|
|
267
|
+
var scene: GdhSceneFile = opened[0]
|
|
257
268
|
if opened[1] == null:
|
|
258
269
|
return opened[2]
|
|
259
270
|
var anim_tree: AnimationTree = opened[1]
|
|
260
|
-
return _read_in(scene,
|
|
271
|
+
return _read_in(scene, GdhAnimationReads.states(anim_tree, state_machine_path))
|
|
261
272
|
|
|
262
273
|
|
|
263
274
|
# =============================================================================
|
|
@@ -280,7 +291,7 @@ func create_animation(args: Dictionary) -> Dictionary:
|
|
|
280
291
|
return {"ok": false, "error": "Unsupported loopMode: " + loop_mode_name}
|
|
281
292
|
|
|
282
293
|
var opened: Array = _open_at(scene_path, player_node_path, "AnimationPlayer")
|
|
283
|
-
var scene:
|
|
294
|
+
var scene: GdhSceneFile = opened[0]
|
|
284
295
|
if opened[1] == null:
|
|
285
296
|
return opened[2]
|
|
286
297
|
var player: AnimationPlayer = opened[1]
|
|
@@ -295,9 +306,9 @@ func create_animation(args: Dictionary) -> Dictionary:
|
|
|
295
306
|
return scene.refuse(str(homes[1]))
|
|
296
307
|
|
|
297
308
|
var anim: Animation = Animation.new()
|
|
298
|
-
anim.length =
|
|
299
|
-
anim.loop_mode =
|
|
300
|
-
anim.step =
|
|
309
|
+
anim.length = GdhRead.as_float(args.get("length", 1.0), 1.0)
|
|
310
|
+
anim.loop_mode = GdhRead.as_int(loop_modes[loop_mode_name]) as Animation.LoopMode
|
|
311
|
+
anim.step = GdhRead.as_float(args.get("step", 0.1), 0.1)
|
|
301
312
|
var called: String = animation_name.get_slice("/", 1) if animation_name.contains("/") else animation_name
|
|
302
313
|
var add_err: Error = anim_lib.add_animation(StringName(called), anim)
|
|
303
314
|
if add_err != OK:
|
|
@@ -337,7 +348,7 @@ func add_animation_track(args: Dictionary) -> Dictionary:
|
|
|
337
348
|
return {"ok": false, "error": keyed[1]}
|
|
338
349
|
|
|
339
350
|
var opened: Array = _open_at(scene_path, player_node_path, "AnimationPlayer")
|
|
340
|
-
var scene:
|
|
351
|
+
var scene: GdhSceneFile = opened[0]
|
|
341
352
|
if opened[1] == null:
|
|
342
353
|
return opened[2]
|
|
343
354
|
var player: AnimationPlayer = opened[1]
|
|
@@ -429,7 +440,7 @@ func _add_track(
|
|
|
429
440
|
|
|
430
441
|
for index: int in keyframes.size():
|
|
431
442
|
var keyframe: Dictionary = keyframes[index]
|
|
432
|
-
if anim.track_insert_key(track_idx,
|
|
443
|
+
if anim.track_insert_key(track_idx, GdhRead.as_float(keyframe["time"]), keys[index]) < 0:
|
|
433
444
|
return [-1, "Keyframe %d could not be inserted." % index, null]
|
|
434
445
|
return [track_idx, "", method_found]
|
|
435
446
|
|
|
@@ -450,7 +461,7 @@ func _keyframes(track: Dictionary) -> Array:
|
|
|
450
461
|
var time: Variant = keyframe.get("time")
|
|
451
462
|
if not (time is float or time is int):
|
|
452
463
|
return [false, "keyframes[%d] has no time." % index]
|
|
453
|
-
var seconds: float =
|
|
464
|
+
var seconds: float = GdhRead.as_float(time)
|
|
454
465
|
if times.has(seconds):
|
|
455
466
|
return [
|
|
456
467
|
false, "keyframes[%d] and [%d] are both at %s seconds." % [times[seconds], index, seconds]
|
|
@@ -504,7 +515,7 @@ func add_animation_state(args: Dictionary) -> Dictionary:
|
|
|
504
515
|
return {"ok": false, "error": "Missing " + str(required[1])}
|
|
505
516
|
|
|
506
517
|
var opened: Array = _open_at(scene_path, anim_tree_path, "AnimationTree")
|
|
507
|
-
var scene:
|
|
518
|
+
var scene: GdhSceneFile = opened[0]
|
|
508
519
|
if opened[1] == null:
|
|
509
520
|
return opened[2]
|
|
510
521
|
var anim_tree: AnimationTree = opened[1]
|
|
@@ -547,7 +558,7 @@ func add_animation_state(args: Dictionary) -> Dictionary:
|
|
|
547
558
|
"animationName": animation_name,
|
|
548
559
|
# False when the tree has no player to look the animation up in.
|
|
549
560
|
"animationFound": player != null,
|
|
550
|
-
"position":
|
|
561
|
+
"position": GdhSerialisation.new().serialize_value(position),
|
|
551
562
|
}
|
|
552
563
|
var files: Dictionary = machine[1]
|
|
553
564
|
return _finish(scene, Callable(), files, answer)
|
|
@@ -580,13 +591,13 @@ func connect_animation_states(args: Dictionary) -> Dictionary:
|
|
|
580
591
|
return {"ok": false, "error": "Unsupported transitionType: " + transition_type}
|
|
581
592
|
var transition: AnimationNodeStateMachineTransition = AnimationNodeStateMachineTransition.new()
|
|
582
593
|
transition.switch_mode = (
|
|
583
|
-
|
|
594
|
+
GdhRead.as_int(modes[transition_type]) as AnimationNodeStateMachineTransition.SwitchMode
|
|
584
595
|
)
|
|
585
596
|
if not advance_condition.is_empty():
|
|
586
597
|
transition.advance_condition = StringName(advance_condition)
|
|
587
598
|
|
|
588
599
|
var opened: Array = _open_at(scene_path, anim_tree_path, "AnimationTree")
|
|
589
|
-
var scene:
|
|
600
|
+
var scene: GdhSceneFile = opened[0]
|
|
590
601
|
if opened[1] == null:
|
|
591
602
|
return opened[2]
|
|
592
603
|
var anim_tree: AnimationTree = opened[1]
|
|
@@ -4,8 +4,8 @@ extends Node
|
|
|
4
4
|
## The editor's view of the files on disk: scans, the reimports asked for, where each stands, and
|
|
5
5
|
## files moved under it.
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const GdhRead = preload("../reading.gd")
|
|
8
|
+
const GdhSceneFile = preload("../scene_file.gd")
|
|
9
9
|
|
|
10
10
|
var _editor_plugin: EditorPlugin = null
|
|
11
11
|
|
|
@@ -63,7 +63,7 @@ func rescan_filesystem(args: Dictionary) -> Dictionary:
|
|
|
63
63
|
var started: bool = false
|
|
64
64
|
var completed_before: int = _scans_completed
|
|
65
65
|
var uids: Dictionary = {"reread": [], "duplicated": []}
|
|
66
|
-
if not
|
|
66
|
+
if not GdhRead.as_bool(args.get("statusOnly", false)) and not busy:
|
|
67
67
|
uids = _reread_changed_uids(filesystem)
|
|
68
68
|
# Files the caller has just written, moved or deleted, read again one by one. The scan finds a
|
|
69
69
|
# script that appeared and leaves the global class of one that has gone in the editor's list,
|
|
@@ -172,7 +172,7 @@ func scan_status(_args: Dictionary) -> Dictionary:
|
|
|
172
172
|
"ok": true,
|
|
173
173
|
"scanning": filesystem.is_scanning(),
|
|
174
174
|
"importing": filesystem.is_importing(),
|
|
175
|
-
"pending":
|
|
175
|
+
"pending": GdhSceneFile.scan_due,
|
|
176
176
|
"sinceScanFinishedMs":
|
|
177
177
|
-1 if _scan_finished_at_msec < 0 else Time.get_ticks_msec() - _scan_finished_at_msec,
|
|
178
178
|
"reimportsCompleted": _reimports_completed,
|
|
@@ -9,7 +9,7 @@ extends Node
|
|
|
9
9
|
## tools answer at all.
|
|
10
10
|
|
|
11
11
|
## What the display server calls itself when the engine was started with no display at all.
|
|
12
|
-
const
|
|
12
|
+
const GdhRead = preload("../reading.gd")
|
|
13
13
|
|
|
14
14
|
const HEADLESS_DISPLAY: String = "headless"
|
|
15
15
|
## Where Godot keeps the port its own debugger listens on for a game it is playing. One setting
|
|
@@ -245,7 +245,7 @@ func playing_status(_args: Dictionary) -> Dictionary:
|
|
|
245
245
|
# play, so the one from when this editor greeted the server is a number it has moved off.
|
|
246
246
|
var settings: EditorSettings = EditorInterface.get_editor_settings()
|
|
247
247
|
var debugger: int = (
|
|
248
|
-
|
|
248
|
+
GdhRead.as_int(settings.get_setting(DEBUGGER_SETTING))
|
|
249
249
|
if settings != null and settings.has_setting(DEBUGGER_SETTING)
|
|
250
250
|
else 0
|
|
251
251
|
)
|