gdharness 1.3.41 → 1.3.43
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 +131 -47
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
- 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 +114 -39
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
- 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 +37 -12
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
- 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 +41 -28
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
- 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 +181 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
- 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 +66 -0
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
- 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 +37 -12
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
- 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 +38 -20
- 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 +211 -0
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
- 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 +84 -35
- 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 +139 -93
- package/build/godot/operations/editor_tools.gd +9 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +114 -39
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +105 -66
- package/build/godot/operations/gdscript_authoring.gd +138 -39
- 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 +63 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +144 -27
- 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 +37 -12
- 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/package.json +1 -1
|
@@ -14,15 +14,6 @@ const RUNTIME: String = "res://addons/gdharness_runtime/runtime_autoload.gd"
|
|
|
14
14
|
var _runtime: Node
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
## Whether gdharness's runtime is listening in this engine: false in a release build, a script run
|
|
18
|
-
## and an engine without the addon, though this node is in every one of them.
|
|
19
|
-
func serving() -> bool:
|
|
20
|
-
if not is_instance_valid(_runtime) or not _runtime.has_method("serving"):
|
|
21
|
-
return false
|
|
22
|
-
var answered: Variant = _runtime.call("serving")
|
|
23
|
-
return answered is bool and answered
|
|
24
|
-
|
|
25
|
-
|
|
26
17
|
func _ready() -> void:
|
|
27
18
|
if not OS.is_debug_build() or not ResourceLoader.exists(RUNTIME):
|
|
28
19
|
return
|
|
@@ -38,3 +29,12 @@ func _ready() -> void:
|
|
|
38
29
|
# Added now, it announces before the main scene runs, as it does when an entry names it.
|
|
39
30
|
add_child(runtime)
|
|
40
31
|
_runtime = runtime
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Whether gdharness's runtime is listening in this engine: false in a release build, a script run
|
|
35
|
+
## and an engine without the addon, though this node is in every one of them.
|
|
36
|
+
func serving() -> bool:
|
|
37
|
+
if not is_instance_valid(_runtime) or not _runtime.has_method("serving"):
|
|
38
|
+
return false
|
|
39
|
+
var answered: Variant = _runtime.call("serving")
|
|
40
|
+
return answered is bool and answered
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
##
|
|
5
|
-
## stores, which runs no script of the scene's, so a read answers the same either way.
|
|
2
|
+
## Animations and state machines read off their nodes: by the editor's tools from the scene it
|
|
3
|
+
## holds, and by a headless engine with none connected from a bare node of the class given what the
|
|
4
|
+
## file stores, which runs no script of the scene's, so a read answers the same either way.
|
|
6
5
|
|
|
7
6
|
const GdhFileReads = preload("file_reads.gd")
|
|
8
7
|
const GdhRead = preload("reading.gd")
|
|
9
8
|
const GdhSerialisation = preload("serialisation.gd")
|
|
10
|
-
|
|
11
9
|
const LOOP_MODE_NAMES: Dictionary[int, String] = {
|
|
12
10
|
Animation.LOOP_NONE: "none",
|
|
13
11
|
Animation.LOOP_LINEAR: "linear",
|
|
@@ -37,7 +35,9 @@ const ADVANCE_MODE_NAMES: Dictionary[int, String] = {
|
|
|
37
35
|
|
|
38
36
|
|
|
39
37
|
## [method animations] of the AnimationPlayer at [param player_path] in the file [param scene_path].
|
|
40
|
-
static func scene_animations(
|
|
38
|
+
static func scene_animations(
|
|
39
|
+
scene_path: String, player_path: String, animation_name: String
|
|
40
|
+
) -> Dictionary:
|
|
41
41
|
var made: Array = _bare(scene_path, player_path, "AnimationPlayer")
|
|
42
42
|
if made[0] == null:
|
|
43
43
|
return made[1]
|
|
@@ -48,7 +48,9 @@ static func scene_animations(scene_path: String, player_path: String, animation_
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
## [method states] of the AnimationTree at [param tree_path] in the file [param scene_path].
|
|
51
|
-
static func scene_states(
|
|
51
|
+
static func scene_states(
|
|
52
|
+
scene_path: String, tree_path: String, state_machine_path: String
|
|
53
|
+
) -> Dictionary:
|
|
52
54
|
var made: Array = _bare(scene_path, tree_path, "AnimationTree")
|
|
53
55
|
if made[0] == null:
|
|
54
56
|
return made[1]
|
|
@@ -64,7 +66,13 @@ static func scene_states(scene_path: String, tree_path: String, state_machine_pa
|
|
|
64
66
|
player_path = "."
|
|
65
67
|
var found: Array = _bare(scene_path, player_path, "AnimationPlayer")
|
|
66
68
|
player = found[0]
|
|
67
|
-
var
|
|
69
|
+
var reached: Node = null
|
|
70
|
+
if player == null and not player_path.is_empty():
|
|
71
|
+
reached = _bare(scene_path, player_path, "Node")[0]
|
|
72
|
+
var unplayed: String = no_player(str(tree.anim_player), reached)
|
|
73
|
+
if reached != null:
|
|
74
|
+
reached.free()
|
|
75
|
+
var answer: Dictionary = states(tree, state_machine_path, player, player_path, unplayed)
|
|
68
76
|
tree.free()
|
|
69
77
|
if player != null:
|
|
70
78
|
player.free()
|
|
@@ -98,9 +106,9 @@ static func animations(player: AnimationPlayer, animation_name: String) -> Dicti
|
|
|
98
106
|
summary["animationName"] = called
|
|
99
107
|
summary["tracks"] = listed_one.get_track_count()
|
|
100
108
|
listed.append(summary)
|
|
101
|
-
return {"ok": true, "animations": listed}
|
|
109
|
+
return { "ok": true, "animations": listed }
|
|
102
110
|
if not player.has_animation(StringName(animation_name)):
|
|
103
|
-
return {"ok": false, "error": no_animation(player, str(player.name), animation_name)}
|
|
111
|
+
return { "ok": false, "error": no_animation(player, str(player.name), animation_name) }
|
|
104
112
|
var anim: Animation = player.get_animation(StringName(animation_name))
|
|
105
113
|
var answer: Dictionary = _summary(anim)
|
|
106
114
|
answer["ok"] = true
|
|
@@ -152,7 +160,8 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
|
|
|
152
160
|
var kind: int = anim.track_get_type(track)
|
|
153
161
|
var path: NodePath = anim.track_get_path(track)
|
|
154
162
|
var shaped: Dictionary = {
|
|
155
|
-
"type": TRACK_TYPE_NAMES.get(kind, str(kind)),
|
|
163
|
+
"type": TRACK_TYPE_NAMES.get(kind, str(kind)),
|
|
164
|
+
"nodePath": str(path.get_concatenated_names()),
|
|
156
165
|
}
|
|
157
166
|
var property: String = str(path.get_concatenated_subnames())
|
|
158
167
|
if not property.is_empty():
|
|
@@ -160,7 +169,7 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
|
|
|
160
169
|
var keyframes: Array[Dictionary] = []
|
|
161
170
|
var methods: Dictionary = {}
|
|
162
171
|
for key: int in anim.track_get_key_count(track):
|
|
163
|
-
var keyframe: Dictionary = {"time": held_seconds(anim.track_get_key_time(track, key))}
|
|
172
|
+
var keyframe: Dictionary = { "time": held_seconds(anim.track_get_key_time(track, key)) }
|
|
164
173
|
if kind == Animation.TYPE_METHOD:
|
|
165
174
|
var method: String = str(anim.method_track_get_name(track, key))
|
|
166
175
|
methods[method] = true
|
|
@@ -182,7 +191,9 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
|
|
|
182
191
|
## The state machine at a slash-separated path of nested state machine names in [param tree], or
|
|
183
192
|
## null when any step of the path is not one. Every node in a tree of state machines is a state
|
|
184
193
|
## machine itself, so the walk asks each step for the child by name and refuses anything else.
|
|
185
|
-
static func state_machine(
|
|
194
|
+
static func state_machine(
|
|
195
|
+
tree: AnimationTree, state_machine_path: String
|
|
196
|
+
) -> AnimationNodeStateMachine:
|
|
186
197
|
if tree == null:
|
|
187
198
|
return null
|
|
188
199
|
var current: AnimationNodeStateMachine = tree.tree_root as AnimationNodeStateMachine
|
|
@@ -201,8 +212,8 @@ static func no_machine(tree: AnimationTree, state_machine_path: String) -> Strin
|
|
|
201
212
|
if tree == null or tree.tree_root == null:
|
|
202
213
|
return (
|
|
203
214
|
"The AnimationTree has no tree_root. Give it one with scene_node set: tree_root "
|
|
204
|
-
+ '{"_type": "AnimationNodeStateMachine"} builds one into the scene, and the res://
|
|
205
|
-
+ "of one made with resource_edit create keeps it in a file of its own."
|
|
215
|
+
+ '{"_type": "AnimationNodeStateMachine"} builds one into the scene, and the res:// '
|
|
216
|
+
+ "path of one made with resource_edit create keeps it in a file of its own."
|
|
206
217
|
)
|
|
207
218
|
var current: AnimationNodeStateMachine = tree.tree_root as AnimationNodeStateMachine
|
|
208
219
|
if current == null:
|
|
@@ -236,42 +247,74 @@ static func no_machine(tree: AnimationTree, state_machine_path: String) -> Strin
|
|
|
236
247
|
return "No state machine at " + state_machine_path
|
|
237
248
|
|
|
238
249
|
|
|
250
|
+
## Why no state finds its animation when [param anim_player], an AnimationTree's path to its player,
|
|
251
|
+
## reaches no AnimationPlayer: [param reached] is the node it reaches instead, null for none.
|
|
252
|
+
static func no_player(anim_player: String, reached: Node) -> String:
|
|
253
|
+
var until: String = "so no state plays anything until scene_node set points anim_player at one."
|
|
254
|
+
if anim_player.is_empty():
|
|
255
|
+
return "The AnimationTree has no anim_player, " + until
|
|
256
|
+
if reached == null:
|
|
257
|
+
return "The AnimationTree's anim_player %s reaches no node, %s" % [anim_player, until]
|
|
258
|
+
return (
|
|
259
|
+
"The AnimationTree's anim_player %s reaches %s, a %s rather than an AnimationPlayer, %s"
|
|
260
|
+
% [anim_player, reached.name, reached.get_class(), until]
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
239
264
|
## Why a state playing [param animation_name] from [param player], at [param player_path] from the
|
|
240
265
|
## scene root as playerNodePath takes it, plays nothing yet. anim_player is relative to the tree
|
|
241
266
|
## ("../Anim"), which no argument takes.
|
|
242
|
-
static func missing_animation(
|
|
267
|
+
static func missing_animation(
|
|
268
|
+
player: AnimationPlayer, player_path: String, animation_name: String
|
|
269
|
+
) -> String:
|
|
243
270
|
var said: String = (
|
|
244
|
-
|
|
271
|
+
(
|
|
272
|
+
"The AnimationPlayer at %s has no animation %s yet, so the state plays nothing until "
|
|
273
|
+
+ "it has one."
|
|
274
|
+
)
|
|
245
275
|
% [player_path, animation_name]
|
|
246
276
|
)
|
|
247
277
|
var close: PackedStringArray = GdhRead.nearest(animation_name, player.get_animation_list())
|
|
248
278
|
if close.is_empty():
|
|
249
279
|
return said
|
|
250
280
|
return (
|
|
251
|
-
said
|
|
281
|
+
said
|
|
282
|
+
+ (
|
|
283
|
+
" The nearest %s %s."
|
|
284
|
+
% ["it has is" if close.size() == 1 else "it has are", ", ".join(close)]
|
|
285
|
+
)
|
|
252
286
|
)
|
|
253
287
|
|
|
254
288
|
|
|
255
289
|
## The states and transitions of the state machine at [param state_machine_path] in [param tree].
|
|
256
290
|
## A state playing an animation says whether [param player], at [param player_path], has it, as
|
|
257
|
-
## add_state answers: a state naming one it lacks plays nothing, and says so only in a log.
|
|
291
|
+
## add_state answers: a state naming one it lacks plays nothing, and says so only in a log. With no
|
|
292
|
+
## player, each says [param unplayed], what [method no_player] made of the tree's path.
|
|
258
293
|
static func states(
|
|
259
|
-
tree: AnimationTree,
|
|
294
|
+
tree: AnimationTree,
|
|
295
|
+
state_machine_path: String,
|
|
296
|
+
player: AnimationPlayer,
|
|
297
|
+
player_path: String,
|
|
298
|
+
unplayed: String
|
|
260
299
|
) -> Dictionary:
|
|
261
300
|
var sm: AnimationNodeStateMachine = state_machine(tree, state_machine_path)
|
|
262
301
|
if sm == null:
|
|
263
|
-
return {"ok": false, "error": no_machine(tree, state_machine_path)}
|
|
302
|
+
return { "ok": false, "error": no_machine(tree, state_machine_path) }
|
|
264
303
|
var listed: Array[Dictionary] = []
|
|
265
304
|
for state: StringName in sm.get_node_list():
|
|
266
305
|
var node: AnimationNode = sm.get_node(state)
|
|
267
|
-
var entry: Dictionary = {"stateName": str(state), "type": node.get_class()}
|
|
306
|
+
var entry: Dictionary = { "stateName": str(state), "type": node.get_class() }
|
|
268
307
|
if node is AnimationNodeAnimation:
|
|
269
308
|
var plays: AnimationNodeAnimation = node
|
|
270
309
|
entry["animationName"] = str(plays.animation)
|
|
271
310
|
var found: bool = player != null and player.has_animation(plays.animation)
|
|
272
311
|
entry["animationFound"] = found
|
|
273
|
-
if player
|
|
274
|
-
entry["animationMissing"] =
|
|
312
|
+
if player == null:
|
|
313
|
+
entry["animationMissing"] = unplayed
|
|
314
|
+
elif not found:
|
|
315
|
+
entry["animationMissing"] = missing_animation(
|
|
316
|
+
player, player_path, str(plays.animation)
|
|
317
|
+
)
|
|
275
318
|
listed.append(entry)
|
|
276
319
|
var transitions: Array[Dictionary] = []
|
|
277
320
|
for index: int in sm.get_transition_count():
|
|
@@ -279,8 +322,10 @@ static func states(
|
|
|
279
322
|
var shaped: Dictionary = {
|
|
280
323
|
"from": str(sm.get_transition_from(index)),
|
|
281
324
|
"to": str(sm.get_transition_to(index)),
|
|
282
|
-
"transitionType":
|
|
283
|
-
|
|
325
|
+
"transitionType":
|
|
326
|
+
SWITCH_MODE_NAMES.get(transition.switch_mode, str(transition.switch_mode)),
|
|
327
|
+
"advanceMode":
|
|
328
|
+
ADVANCE_MODE_NAMES.get(transition.advance_mode, str(transition.advance_mode)),
|
|
284
329
|
}
|
|
285
330
|
var condition: String = str(transition.advance_condition)
|
|
286
331
|
if not condition.is_empty():
|
|
@@ -290,9 +335,9 @@ static func states(
|
|
|
290
335
|
if not condition.is_empty() and transition.advance_mode != transition.ADVANCE_MODE_AUTO:
|
|
291
336
|
shaped["conditionNote"] = (
|
|
292
337
|
(
|
|
293
|
-
"advanceMode is %s, so %s never advances it: the engine checks a condition
|
|
294
|
-
+ " auto transition%s. disconnect_states and connect_states again
|
|
295
|
-
+ " write it as auto"
|
|
338
|
+
"advanceMode is %s, so %s never advances it: the engine checks a condition "
|
|
339
|
+
+ "only on an auto transition%s. disconnect_states and connect_states again "
|
|
340
|
+
+ "with the condition write it as auto"
|
|
296
341
|
)
|
|
297
342
|
% [
|
|
298
343
|
shaped["advanceMode"],
|
|
@@ -301,16 +346,16 @@ static func states(
|
|
|
301
346
|
", and travel() still takes this one"
|
|
302
347
|
if transition.advance_mode == transition.ADVANCE_MODE_ENABLED
|
|
303
348
|
else ""
|
|
304
|
-
)
|
|
349
|
+
),
|
|
305
350
|
]
|
|
306
351
|
)
|
|
307
352
|
transitions.append(shaped)
|
|
308
|
-
return {"ok": true, "states": listed, "transitions": transitions}
|
|
353
|
+
return { "ok": true, "states": listed, "transitions": transitions }
|
|
309
354
|
|
|
310
355
|
|
|
311
356
|
## The states of the AnimationTrees under [param root] that play [param animation_name] from
|
|
312
|
-
## [param player], as [[node, {animTreePath, state}]], a state named by the path of the nodes
|
|
313
|
-
## to it.
|
|
357
|
+
## [param player], as [[node, {animTreePath, state}]], a state named by the path of the nodes
|
|
358
|
+
## leading to it.
|
|
314
359
|
static func states_playing(root: Node, player: AnimationPlayer, animation_name: String) -> Array:
|
|
315
360
|
var found: Array = []
|
|
316
361
|
var trees: Array[Node] = root.find_children("*", "AnimationTree", true, false)
|
|
@@ -331,7 +376,9 @@ static func _collect_playing(
|
|
|
331
376
|
if node is AnimationNodeAnimation:
|
|
332
377
|
var plays: AnimationNodeAnimation = node
|
|
333
378
|
if plays.animation == StringName(animation_name):
|
|
334
|
-
found.append(
|
|
379
|
+
found.append(
|
|
380
|
+
[plays, { "animTreePath": tree_path, "state": "." if at.is_empty() else at }]
|
|
381
|
+
)
|
|
335
382
|
return
|
|
336
383
|
var children: Dictionary = {}
|
|
337
384
|
if node is AnimationNodeStateMachine:
|
|
@@ -352,4 +399,6 @@ static func _collect_playing(
|
|
|
352
399
|
children[str(point)] = plane.get_blend_point_node(point)
|
|
353
400
|
for step: String in children:
|
|
354
401
|
var child: AnimationNode = children[step]
|
|
355
|
-
_collect_playing(
|
|
402
|
+
_collect_playing(
|
|
403
|
+
child, step if at.is_empty() else at + "/" + step, tree_path, animation_name, found
|
|
404
|
+
)
|
|
@@ -23,7 +23,9 @@ func create_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
23
23
|
# Sends are by name, so a second bus with one would leave every send to it meaning either.
|
|
24
24
|
var existing: int = AudioServer.get_bus_index(bus_name)
|
|
25
25
|
if existing != -1:
|
|
26
|
-
return _log.failure(
|
|
26
|
+
return _log.failure(
|
|
27
|
+
"A bus named " + bus_name + " already exists, at index " + str(existing)
|
|
28
|
+
)
|
|
27
29
|
|
|
28
30
|
# Right after the bus it sends to, because a bus can only send to one before it. The engine
|
|
29
31
|
# inserts there and moves every later bus down one, so the new bus is at that index: taking the
|
|
@@ -41,7 +43,7 @@ func create_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
41
43
|
var buses: Array[Dictionary] = []
|
|
42
44
|
for i: int in range(AudioServer.bus_count):
|
|
43
45
|
buses.append(_bus(i))
|
|
44
|
-
return {"bus": _bus(new_idx), "buses": buses, "layout": layout}
|
|
46
|
+
return { "bus": _bus(new_idx), "buses": buses, "layout": layout }
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
func remove_audio_bus(params: Dictionary) -> Dictionary:
|
|
@@ -52,7 +54,9 @@ func remove_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
52
54
|
if index == -1:
|
|
53
55
|
return _log.failure("No bus named %s. The buses are %s." % [bus_name, ", ".join(_names())])
|
|
54
56
|
if index == 0:
|
|
55
|
-
return _log.failure(
|
|
57
|
+
return _log.failure(
|
|
58
|
+
"%s is the bus every other ends in, so it cannot be removed." % bus_name
|
|
59
|
+
)
|
|
56
60
|
|
|
57
61
|
# A bus sending to this one would be left sending to a name nothing has, which the engine reads
|
|
58
62
|
# as Master; sent on to where this one went instead, the chain it was part of keeps its shape.
|
|
@@ -70,9 +74,9 @@ func remove_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
70
74
|
var buses: Array[Dictionary] = []
|
|
71
75
|
for i: int in range(AudioServer.bus_count):
|
|
72
76
|
buses.append(_bus(i))
|
|
73
|
-
var answer: Dictionary = {"removed": bus_name, "buses": buses, "layout": layout}
|
|
77
|
+
var answer: Dictionary = { "removed": bus_name, "buses": buses, "layout": layout }
|
|
74
78
|
if not resent.is_empty():
|
|
75
|
-
answer["resent"] = {"buses": resent, "to": onward}
|
|
79
|
+
answer["resent"] = { "buses": resent, "to": onward }
|
|
76
80
|
return answer
|
|
77
81
|
|
|
78
82
|
|
|
@@ -81,15 +85,19 @@ func get_audio_buses(_params: Dictionary) -> Dictionary:
|
|
|
81
85
|
for i: int in range(AudioServer.bus_count):
|
|
82
86
|
buses.append(_bus(i))
|
|
83
87
|
var layout: String = _layout_path()
|
|
84
|
-
var answer: Dictionary = {
|
|
88
|
+
var answer: Dictionary = {
|
|
89
|
+
"bus_count": AudioServer.bus_count,
|
|
90
|
+
"buses": buses,
|
|
91
|
+
"layout": layout,
|
|
92
|
+
}
|
|
85
93
|
# The setting names a path whether or not anything is there, and a project that never changed a
|
|
86
94
|
# bus has no file: the path alone read as a layout the project keeps.
|
|
87
95
|
if not FileAccess.file_exists(layout):
|
|
88
96
|
answer["layout_on_disk"] = false
|
|
89
97
|
answer["note"] = (
|
|
90
98
|
(
|
|
91
|
-
"Nothing is at %s, so these are the engine's default buses; the first change to a
|
|
92
|
-
+ "writes the layout there."
|
|
99
|
+
"Nothing is at %s, so these are the engine's default buses; the first change to a "
|
|
100
|
+
+ "bus writes the layout there."
|
|
93
101
|
)
|
|
94
102
|
% layout
|
|
95
103
|
)
|
|
@@ -139,7 +147,10 @@ func set_audio_bus_effect(params: Dictionary) -> Dictionary:
|
|
|
139
147
|
if layout.is_empty():
|
|
140
148
|
return {}
|
|
141
149
|
var answer: Dictionary = {
|
|
142
|
-
"bus": _bus(bus_idx),
|
|
150
|
+
"bus": _bus(bus_idx),
|
|
151
|
+
"effect_index": effect_idx,
|
|
152
|
+
"effect_type": effect.get_class(),
|
|
153
|
+
"layout": layout,
|
|
143
154
|
}
|
|
144
155
|
if not replaced.is_empty():
|
|
145
156
|
answer["replaced"] = replaced
|
|
@@ -157,7 +168,7 @@ func set_audio_bus_volume(params: Dictionary) -> Dictionary:
|
|
|
157
168
|
var layout: String = _save_layout()
|
|
158
169
|
if layout.is_empty():
|
|
159
170
|
return {}
|
|
160
|
-
return {"bus": _bus(bus_idx), "layout": layout}
|
|
171
|
+
return { "bus": _bus(bus_idx), "layout": layout }
|
|
161
172
|
|
|
162
173
|
|
|
163
174
|
## Renames a bus, and every send to it with it: the engine renames the one bus and leaves a bus that
|
|
@@ -177,7 +188,9 @@ func rename_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
177
188
|
)
|
|
178
189
|
var existing: int = AudioServer.get_bus_index(new_name)
|
|
179
190
|
if existing != -1:
|
|
180
|
-
return _log.failure(
|
|
191
|
+
return _log.failure(
|
|
192
|
+
"A bus named " + new_name + " already exists, at index " + str(existing)
|
|
193
|
+
)
|
|
181
194
|
AudioServer.set_bus_name(index, new_name)
|
|
182
195
|
var resent: Array[String] = []
|
|
183
196
|
for i: int in range(AudioServer.bus_count):
|
|
@@ -189,7 +202,7 @@ func rename_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
189
202
|
if layout.is_empty():
|
|
190
203
|
return {}
|
|
191
204
|
var answer: Dictionary = {
|
|
192
|
-
"renamed": {"from": bus_name, "to": new_name}, "bus": _bus(index), "layout": layout
|
|
205
|
+
"renamed": { "from": bus_name, "to": new_name }, "bus": _bus(index), "layout": layout,
|
|
193
206
|
}
|
|
194
207
|
if not resent.is_empty():
|
|
195
208
|
answer["resent"] = resent
|
|
@@ -211,7 +224,10 @@ func set_audio_bus_send(params: Dictionary) -> Dictionary:
|
|
|
211
224
|
return _log.failure("No bus named %s. The buses are %s." % [send, ", ".join(_names())])
|
|
212
225
|
if target == bus_idx:
|
|
213
226
|
return _log.failure(
|
|
214
|
-
|
|
227
|
+
(
|
|
228
|
+
"%s cannot send to itself; it can send to %s."
|
|
229
|
+
% [bus_name, ", ".join(_names().slice(0, bus_idx))]
|
|
230
|
+
)
|
|
215
231
|
)
|
|
216
232
|
if target > bus_idx:
|
|
217
233
|
return (
|
|
@@ -219,10 +235,17 @@ func set_audio_bus_send(params: Dictionary) -> Dictionary:
|
|
|
219
235
|
. failure(
|
|
220
236
|
(
|
|
221
237
|
(
|
|
222
|
-
"%s is bus %d and %s is bus %d: a bus sends only to one before it, which
|
|
223
|
-
+ "going round in a loop, so %s can send to %s."
|
|
238
|
+
"%s is bus %d and %s is bus %d: a bus sends only to one before it, which "
|
|
239
|
+
+ "keeps sound from going round in a loop, so %s can send to %s."
|
|
224
240
|
)
|
|
225
|
-
% [
|
|
241
|
+
% [
|
|
242
|
+
bus_name,
|
|
243
|
+
bus_idx,
|
|
244
|
+
send,
|
|
245
|
+
target,
|
|
246
|
+
bus_name,
|
|
247
|
+
", ".join(_names().slice(0, bus_idx)),
|
|
248
|
+
]
|
|
226
249
|
)
|
|
227
250
|
)
|
|
228
251
|
)
|
|
@@ -231,7 +254,7 @@ func set_audio_bus_send(params: Dictionary) -> Dictionary:
|
|
|
231
254
|
var layout: String = _save_layout()
|
|
232
255
|
if layout.is_empty():
|
|
233
256
|
return {}
|
|
234
|
-
return {"bus": _bus(bus_idx), "layout": layout}
|
|
257
|
+
return { "bus": _bus(bus_idx), "layout": layout }
|
|
235
258
|
|
|
236
259
|
|
|
237
260
|
func _names() -> Array[String]:
|
|
@@ -270,7 +293,11 @@ func _bus(index: int) -> Dictionary:
|
|
|
270
293
|
|
|
271
294
|
|
|
272
295
|
func _layout_path() -> String:
|
|
273
|
-
return str(
|
|
296
|
+
return str(
|
|
297
|
+
ProjectSettings.get_setting(
|
|
298
|
+
"audio/buses/default_bus_layout", "res://default_bus_layout.tres"
|
|
299
|
+
)
|
|
300
|
+
)
|
|
274
301
|
|
|
275
302
|
|
|
276
303
|
# The path the layout was written to, or empty with the reason on stderr.
|
|
@@ -285,7 +312,9 @@ func _save_layout() -> String:
|
|
|
285
312
|
|
|
286
313
|
# Any effect class the engine has, by its class name or the part after "AudioEffect".
|
|
287
314
|
func _effect_named(effect_type: String) -> AudioEffect:
|
|
288
|
-
var cls: String =
|
|
315
|
+
var cls: String = (
|
|
316
|
+
effect_type if effect_type.begins_with("AudioEffect") else "AudioEffect" + effect_type
|
|
317
|
+
)
|
|
289
318
|
if not ClassDB.class_exists(cls) or not ClassDB.is_parent_class(cls, "AudioEffect"):
|
|
290
319
|
return null
|
|
291
320
|
if not ClassDB.can_instantiate(cls):
|
|
@@ -9,7 +9,6 @@ const GdhPatterns = preload("patterns.gd")
|
|
|
9
9
|
const GdhRead = preload("reading.gd")
|
|
10
10
|
const GdhFileWalk = preload("file_walk.gd")
|
|
11
11
|
const GdhLog = preload("logger.gd")
|
|
12
|
-
|
|
13
12
|
const CACHE_PATH: String = "res://.godot/global_script_class_cache.cfg"
|
|
14
13
|
|
|
15
14
|
var _log: GdhLog
|
|
@@ -30,9 +29,9 @@ func refresh_class_cache(_params: Dictionary) -> Dictionary:
|
|
|
30
29
|
var entry: Dictionary = _entry_for(path, skipped)
|
|
31
30
|
if not entry.is_empty():
|
|
32
31
|
entries.append(entry)
|
|
33
|
-
# Another language's classes are kept while their files are there. Only GDScript is read here
|
|
34
|
-
# the list is written whole, so a C# project's global classes were dropped from it and
|
|
35
|
-
# removed, and nothing started next could resolve them.
|
|
32
|
+
# Another language's classes are kept while their files are there. Only GDScript is read here
|
|
33
|
+
# and the list is written whole, so a C# project's global classes were dropped from it and
|
|
34
|
+
# answered as removed, and nothing started next could resolve them.
|
|
36
35
|
var carried: Array[String] = []
|
|
37
36
|
for entry: Variant in cached:
|
|
38
37
|
if not entry is Dictionary:
|
|
@@ -43,7 +42,9 @@ func refresh_class_cache(_params: Dictionary) -> Dictionary:
|
|
|
43
42
|
entries.append(listed)
|
|
44
43
|
carried.append(str(listed.get("class", "")))
|
|
45
44
|
# By name as text: StringName's own order is by identity, not by spelling.
|
|
46
|
-
entries.sort_custom(
|
|
45
|
+
entries.sort_custom(
|
|
46
|
+
func(a: Dictionary, b: Dictionary) -> bool: return str(a["class"]) < str(b["class"])
|
|
47
|
+
)
|
|
47
48
|
|
|
48
49
|
var after: Dictionary = _entries_by_class(entries)
|
|
49
50
|
var config: ConfigFile = ConfigFile.new()
|
|
@@ -52,7 +53,9 @@ func refresh_class_cache(_params: Dictionary) -> Dictionary:
|
|
|
52
53
|
ProjectSettings.globalize_path(CACHE_PATH.get_base_dir())
|
|
53
54
|
)
|
|
54
55
|
if made != OK and made != ERR_ALREADY_EXISTS:
|
|
55
|
-
return _log.failure(
|
|
56
|
+
return _log.failure(
|
|
57
|
+
"Could not make " + CACHE_PATH.get_base_dir() + ": " + error_string(made)
|
|
58
|
+
)
|
|
56
59
|
var err: Error = config.save(CACHE_PATH)
|
|
57
60
|
if err != OK:
|
|
58
61
|
return _log.failure("Failed to write " + CACHE_PATH + ": " + error_string(err))
|
|
@@ -139,7 +142,7 @@ func _base_of(path: String, header: Dictionary, skipped: Array[Dictionary]) -> V
|
|
|
139
142
|
return ""
|
|
140
143
|
var reached: String = opened + ":" + ".".join(PackedStringArray(names))
|
|
141
144
|
if reached in visited:
|
|
142
|
-
skipped.append({"path": path, "reason": "extends itself through " + parent_path})
|
|
145
|
+
skipped.append({ "path": path, "reason": "extends itself through " + parent_path })
|
|
143
146
|
return null
|
|
144
147
|
visited.append(reached)
|
|
145
148
|
var lines: PackedStringArray = FileAccess.get_file_as_string(opened).split("\n")
|
|
@@ -162,7 +165,7 @@ static func _extends_parts(text: String) -> Dictionary:
|
|
|
162
165
|
if rest.begins_with('"') or rest.begins_with("'"):
|
|
163
166
|
var closing: int = rest.find(rest[0], 1)
|
|
164
167
|
if closing == -1:
|
|
165
|
-
return {"path": "", "names": [] as Array[String]}
|
|
168
|
+
return { "path": "", "names": [] as Array[String] }
|
|
166
169
|
path = rest.substr(1, closing - 1)
|
|
167
170
|
rest = rest.substr(closing + 1)
|
|
168
171
|
var names: Array[String] = []
|
|
@@ -179,7 +182,7 @@ static func _extends_parts(text: String) -> Dictionary:
|
|
|
179
182
|
break
|
|
180
183
|
names.append(found.get_string(1))
|
|
181
184
|
rest = rest.substr(found.get_end())
|
|
182
|
-
return {"path": path, "names": names}
|
|
185
|
+
return { "path": path, "names": names }
|
|
183
186
|
|
|
184
187
|
|
|
185
188
|
# The file the engine's class scan reads for an `extends` path. It opens the path as written, and
|
|
@@ -203,7 +206,7 @@ func _inner_class(lines: PackedStringArray, names: Array[String]) -> Dictionary:
|
|
|
203
206
|
var found: Dictionary = {}
|
|
204
207
|
for name: String in names:
|
|
205
208
|
var at: int = -1
|
|
206
|
-
found = {"found": true}
|
|
209
|
+
found = { "found": true }
|
|
207
210
|
for index: int in range(start, end):
|
|
208
211
|
var text: String = lines[index].strip_edges()
|
|
209
212
|
if text.is_empty() or text.begins_with("#") or _indent(lines[index]) != depth:
|
|
@@ -255,7 +258,9 @@ func _header_of(path: String) -> Dictionary:
|
|
|
255
258
|
# which is where the body begins.
|
|
256
259
|
func _header_in(lines: PackedStringArray) -> Dictionary:
|
|
257
260
|
var header: Dictionary = {}
|
|
258
|
-
var annotation: RegEx = GdhPatterns.compiled(
|
|
261
|
+
var annotation: RegEx = GdhPatterns.compiled(
|
|
262
|
+
'^@([a-z_]+)(?:\\(\\s*(?:"([^"]*)")?[^)]*\\))?\\s*'
|
|
263
|
+
)
|
|
259
264
|
var extends_line: RegEx = GdhPatterns.compiled("^extends\\s+(.*)$")
|
|
260
265
|
var same_line: RegEx = GdhPatterns.compiled("^\\s+extends\\s+(.*)$")
|
|
261
266
|
for line: String in lines:
|