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
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
|
-
##
|
|
5
|
-
## headless engine.
|
|
3
|
+
## Animations and animation trees, edited in the open editor or, for a project no editor serves, by
|
|
4
|
+
## a headless engine.
|
|
6
5
|
|
|
7
6
|
const GdhAnimationReads = preload("../animation_reads.gd")
|
|
8
7
|
const GdhHeadlessCheck = preload("../headless_check.gd")
|
|
@@ -11,9 +10,10 @@ const GdhRead = preload("../reading.gd")
|
|
|
11
10
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
12
11
|
const GdhSceneUids = preload("../scene_uids.gd")
|
|
13
12
|
const GdhSerialisation = preload("../serialisation.gd")
|
|
14
|
-
|
|
15
13
|
const LOOP_MODES: Dictionary = {
|
|
16
|
-
"none": Animation.LOOP_NONE,
|
|
14
|
+
"none": Animation.LOOP_NONE,
|
|
15
|
+
"linear": Animation.LOOP_LINEAR,
|
|
16
|
+
"pingpong": Animation.LOOP_PINGPONG,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
var _editor_plugin: EditorPlugin = null
|
|
@@ -74,7 +74,9 @@ func _not_of_kind(scene: GdhSceneFile, node: Node, node_path: String, kind: Stri
|
|
|
74
74
|
|
|
75
75
|
## Writes [param scene] once [param check] passes, then the files of [param homes], answering
|
|
76
76
|
## [param answer] or a refusal.
|
|
77
|
-
func _finish(
|
|
77
|
+
func _finish(
|
|
78
|
+
scene: GdhSceneFile, check: Callable, homes: Dictionary, answer: Dictionary
|
|
79
|
+
) -> Dictionary:
|
|
78
80
|
if not Engine.is_editor_hint():
|
|
79
81
|
for file: Variant in homes:
|
|
80
82
|
var home: Resource = homes[file]
|
|
@@ -85,9 +87,9 @@ func _finish(scene: GdhSceneFile, check: Callable, homes: Dictionary, answer: Di
|
|
|
85
87
|
(
|
|
86
88
|
(
|
|
87
89
|
(
|
|
88
|
-
"Nothing was written: %s holds a resource with a script that
|
|
89
|
-
+ "
|
|
90
|
-
+ "edit. "
|
|
90
|
+
"Nothing was written: %s holds a resource with a script that "
|
|
91
|
+
+ "is not a tool, and what that script changed when a headless "
|
|
92
|
+
+ "engine loaded it cannot be told apart from the edit. "
|
|
91
93
|
)
|
|
92
94
|
% file
|
|
93
95
|
)
|
|
@@ -102,7 +104,7 @@ func _finish(scene: GdhSceneFile, check: Callable, homes: Dictionary, answer: Di
|
|
|
102
104
|
var resource: Resource = homes[file]
|
|
103
105
|
var saving: Error = GdhSceneUids.save_keeping_uid(resource, str(file))
|
|
104
106
|
if saving != OK:
|
|
105
|
-
return {"ok": false, "error": "Failed to save %s: %s" % [file, error_string(saving)]}
|
|
107
|
+
return { "ok": false, "error": "Failed to save %s: %s" % [file, error_string(saving)] }
|
|
106
108
|
if Engine.is_editor_hint():
|
|
107
109
|
EditorInterface.get_resource_filesystem().update_file(str(file))
|
|
108
110
|
return answer
|
|
@@ -112,7 +114,10 @@ func _finish(scene: GdhSceneFile, check: Callable, homes: Dictionary, answer: Di
|
|
|
112
114
|
## engine ran as it loaded the file.
|
|
113
115
|
func _scripted(resource: Resource) -> bool:
|
|
114
116
|
var script: Script = resource.get_script()
|
|
115
|
-
return (
|
|
117
|
+
return (
|
|
118
|
+
(script != null and not script.is_tool())
|
|
119
|
+
or GdhHeadlessCheck.holds_scripted_embedded(resource, {})
|
|
120
|
+
)
|
|
116
121
|
|
|
117
122
|
|
|
118
123
|
func _parse_json_maybe(value: Variant) -> Variant:
|
|
@@ -160,11 +165,11 @@ func _homes(scene: GdhSceneFile, held: Array) -> Array:
|
|
|
160
165
|
false,
|
|
161
166
|
(
|
|
162
167
|
(
|
|
163
|
-
"The %s is kept in %s, not in %s, and an edit here would not be saved there.
|
|
164
|
-
+ "where it is kept instead."
|
|
168
|
+
"The %s is kept in %s, not in %s, and an edit here would not be saved there. "
|
|
169
|
+
+ "Edit it where it is kept instead."
|
|
165
170
|
)
|
|
166
171
|
% [entry[1], at.get_slice("::", 0), scene.path]
|
|
167
|
-
)
|
|
172
|
+
),
|
|
168
173
|
]
|
|
169
174
|
return [true, homes]
|
|
170
175
|
|
|
@@ -198,7 +203,9 @@ func _library_of(player: AnimationPlayer, animation_name: String) -> AnimationLi
|
|
|
198
203
|
## The state machine at [param state_machine_path] in [param anim_tree], with the files it is kept
|
|
199
204
|
## in, as [machine, homes, why not].
|
|
200
205
|
func _machine(scene: GdhSceneFile, anim_tree: AnimationTree, state_machine_path: String) -> Array:
|
|
201
|
-
var sm: AnimationNodeStateMachine = GdhAnimationReads.state_machine(
|
|
206
|
+
var sm: AnimationNodeStateMachine = GdhAnimationReads.state_machine(
|
|
207
|
+
anim_tree, state_machine_path
|
|
208
|
+
)
|
|
202
209
|
if not sm:
|
|
203
210
|
return [null, {}, GdhAnimationReads.no_machine(anim_tree, state_machine_path)]
|
|
204
211
|
var homes: Array = _homes(scene, [[sm, "state machine"]])
|
|
@@ -225,7 +232,7 @@ func read_animations(args: Dictionary) -> Dictionary:
|
|
|
225
232
|
var player_node_path: String = str(args.get("playerNodePath", "."))
|
|
226
233
|
var animation_name: String = str(args.get("animationName", ""))
|
|
227
234
|
if scene_path.strip_edges() == "res://":
|
|
228
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
235
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
229
236
|
|
|
230
237
|
var opened: Array = _read_at(scene_path, player_node_path, "AnimationPlayer")
|
|
231
238
|
var scene: GdhSceneFile = opened[0]
|
|
@@ -250,18 +257,23 @@ func read_animation_states(args: Dictionary) -> Dictionary:
|
|
|
250
257
|
var anim_tree_path: String = str(args.get("animTreePath", ""))
|
|
251
258
|
var state_machine_path: String = str(args.get("stateMachinePath", ""))
|
|
252
259
|
if scene_path.strip_edges() == "res://":
|
|
253
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
260
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
254
261
|
if anim_tree_path.is_empty():
|
|
255
|
-
return {"ok": false, "error": "Missing animTreePath"}
|
|
262
|
+
return { "ok": false, "error": "Missing animTreePath" }
|
|
256
263
|
|
|
257
264
|
var opened: Array = _read_at(scene_path, anim_tree_path, "AnimationTree")
|
|
258
265
|
var scene: GdhSceneFile = opened[0]
|
|
259
266
|
if opened[1] == null:
|
|
260
267
|
return opened[2]
|
|
261
268
|
var anim_tree: AnimationTree = opened[1]
|
|
262
|
-
var
|
|
269
|
+
var reached: Node = anim_tree.get_node_or_null(anim_tree.anim_player)
|
|
270
|
+
var player: AnimationPlayer = reached as AnimationPlayer
|
|
263
271
|
var player_path: String = "" if player == null else scene.path_of(player)
|
|
264
|
-
|
|
272
|
+
var unplayed: String = GdhAnimationReads.no_player(str(anim_tree.anim_player), reached)
|
|
273
|
+
return _read_in(
|
|
274
|
+
scene,
|
|
275
|
+
GdhAnimationReads.states(anim_tree, state_machine_path, player, player_path, unplayed)
|
|
276
|
+
)
|
|
265
277
|
|
|
266
278
|
|
|
267
279
|
# =============================================================================
|
|
@@ -274,18 +286,18 @@ func create_animation(args: Dictionary) -> Dictionary:
|
|
|
274
286
|
var loop_mode_name: String = str(args.get("loopMode", "none"))
|
|
275
287
|
|
|
276
288
|
if scene_path.strip_edges() == "res://":
|
|
277
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
289
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
278
290
|
if animation_name.strip_edges().is_empty():
|
|
279
|
-
return {"ok": false, "error": "Missing animationName"}
|
|
291
|
+
return { "ok": false, "error": "Missing animationName" }
|
|
280
292
|
var named: String = _name_refusal(animation_name)
|
|
281
293
|
if not named.is_empty():
|
|
282
|
-
return {"ok": false, "error": named}
|
|
294
|
+
return { "ok": false, "error": named }
|
|
283
295
|
var loop_modes: Dictionary = LOOP_MODES
|
|
284
296
|
if not loop_modes.has(loop_mode_name):
|
|
285
|
-
return {"ok": false, "error": "Unsupported loopMode: " + loop_mode_name}
|
|
297
|
+
return { "ok": false, "error": "Unsupported loopMode: " + loop_mode_name }
|
|
286
298
|
var timing: String = _timing_refusal(args)
|
|
287
299
|
if not timing.is_empty():
|
|
288
|
-
return {"ok": false, "error": timing}
|
|
300
|
+
return { "ok": false, "error": timing }
|
|
289
301
|
|
|
290
302
|
var opened: Array = _open_at(scene_path, player_node_path, "AnimationPlayer")
|
|
291
303
|
var scene: GdhSceneFile = opened[0]
|
|
@@ -306,7 +318,9 @@ func create_animation(args: Dictionary) -> Dictionary:
|
|
|
306
318
|
anim.length = GdhRead.as_float(args.get("length", 1.0), 1.0)
|
|
307
319
|
anim.loop_mode = GdhRead.as_int(loop_modes[loop_mode_name]) as Animation.LoopMode
|
|
308
320
|
anim.step = GdhRead.as_float(args.get("step", 0.1), 0.1)
|
|
309
|
-
var called: String =
|
|
321
|
+
var called: String = (
|
|
322
|
+
animation_name.get_slice("/", 1) if animation_name.contains("/") else animation_name
|
|
323
|
+
)
|
|
310
324
|
# As the editor names a new one: the file keeps it as resource_name, which the inspector shows.
|
|
311
325
|
anim.resource_name = called
|
|
312
326
|
var add_err: Error = anim_lib.add_animation(StringName(called), anim)
|
|
@@ -323,10 +337,12 @@ func create_animation(args: Dictionary) -> Dictionary:
|
|
|
323
337
|
"ok": true,
|
|
324
338
|
"animationName": animation_name,
|
|
325
339
|
"length": GdhAnimationReads.held_seconds(anim.length),
|
|
326
|
-
"loopMode": loop_mode_name
|
|
340
|
+
"loopMode": loop_mode_name,
|
|
327
341
|
}
|
|
328
342
|
if library[2]:
|
|
329
|
-
answer["libraryMade"] =
|
|
343
|
+
answer["libraryMade"] = (
|
|
344
|
+
animation_name.get_slice("/", 0) if animation_name.contains("/") else ""
|
|
345
|
+
)
|
|
330
346
|
var files: Dictionary = homes[1]
|
|
331
347
|
return _finish(scene, check, files, answer)
|
|
332
348
|
|
|
@@ -337,8 +353,8 @@ static func _name_refusal(animation_name: String) -> String:
|
|
|
337
353
|
if animation_name.count("/") > 1:
|
|
338
354
|
return (
|
|
339
355
|
(
|
|
340
|
-
"%s names a library and an animation in it with one slash, as moves/walk; a
|
|
341
|
-
+ "libraries."
|
|
356
|
+
"%s names a library and an animation in it with one slash, as moves/walk; a "
|
|
357
|
+
+ "library holds no libraries."
|
|
342
358
|
)
|
|
343
359
|
% animation_name
|
|
344
360
|
)
|
|
@@ -368,7 +384,9 @@ static func _past_length(anim: Animation, animation_name: String) -> String:
|
|
|
368
384
|
|
|
369
385
|
## The pastLength sentence for [param past], keys of [param anim] each named by its track's path and
|
|
370
386
|
## time, or "" for none.
|
|
371
|
-
static func _past_sentence(
|
|
387
|
+
static func _past_sentence(
|
|
388
|
+
anim: Animation, animation_name: String, past: PackedStringArray
|
|
389
|
+
) -> String:
|
|
372
390
|
if past.is_empty():
|
|
373
391
|
return ""
|
|
374
392
|
return (
|
|
@@ -403,7 +421,10 @@ static func _timing_refusal(args: Dictionary) -> String:
|
|
|
403
421
|
if args.has("length"):
|
|
404
422
|
var length: Variant = args["length"]
|
|
405
423
|
if not (length is float or length is int) or GdhRead.as_float(length) <= 0.0:
|
|
406
|
-
return
|
|
424
|
+
return (
|
|
425
|
+
"length is the animation's length in seconds, more than 0, and %s is not."
|
|
426
|
+
% _given(length)
|
|
427
|
+
)
|
|
407
428
|
if args.has("step"):
|
|
408
429
|
var step: Variant = args["step"]
|
|
409
430
|
if not (step is float or step is int) or GdhRead.as_float(step) < 0.0:
|
|
@@ -439,16 +460,19 @@ func set_animation(args: Dictionary) -> Dictionary:
|
|
|
439
460
|
var animation_name: String = str(args.get("animationName", ""))
|
|
440
461
|
var loop_mode_name: String = str(args.get("loopMode", ""))
|
|
441
462
|
if scene_path.strip_edges() == "res://":
|
|
442
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
463
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
443
464
|
if animation_name.strip_edges().is_empty():
|
|
444
|
-
return {"ok": false, "error": "Missing animationName"}
|
|
465
|
+
return { "ok": false, "error": "Missing animationName" }
|
|
445
466
|
if not (args.has("length") or args.has("loopMode") or args.has("step")):
|
|
446
|
-
return {
|
|
467
|
+
return {
|
|
468
|
+
"ok": false,
|
|
469
|
+
"error": "set changes length, loopMode or step, and was given none of them.",
|
|
470
|
+
}
|
|
447
471
|
if args.has("loopMode") and not LOOP_MODES.has(loop_mode_name):
|
|
448
|
-
return {"ok": false, "error": "Unsupported loopMode: " + loop_mode_name}
|
|
472
|
+
return { "ok": false, "error": "Unsupported loopMode: " + loop_mode_name }
|
|
449
473
|
var timing: String = _timing_refusal(args)
|
|
450
474
|
if not timing.is_empty():
|
|
451
|
-
return {"ok": false, "error": timing}
|
|
475
|
+
return { "ok": false, "error": timing }
|
|
452
476
|
|
|
453
477
|
var found: Array = _animation_at(scene_path, player_node_path, animation_name)
|
|
454
478
|
if found[1] == null:
|
|
@@ -475,7 +499,10 @@ func set_animation(args: Dictionary) -> Dictionary:
|
|
|
475
499
|
if copy == null or not copy.has_animation(StringName(animation_name)):
|
|
476
500
|
return "%s would not be in the scene when it loads." % animation_name
|
|
477
501
|
if not is_equal_approx(copy.get_animation(StringName(animation_name)).length, length):
|
|
478
|
-
return
|
|
502
|
+
return (
|
|
503
|
+
"%s would not be %s s long when the scene loads."
|
|
504
|
+
% [animation_name, _seconds(length)]
|
|
505
|
+
)
|
|
479
506
|
return ""
|
|
480
507
|
var answer: Dictionary = {
|
|
481
508
|
"ok": true,
|
|
@@ -500,12 +527,14 @@ func rename_animation(args: Dictionary) -> Dictionary:
|
|
|
500
527
|
var animation_name: String = str(args.get("animationName", ""))
|
|
501
528
|
var new_name: String = str(args.get("newName", "")).strip_edges()
|
|
502
529
|
if scene_path.strip_edges() == "res://":
|
|
503
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
530
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
504
531
|
if animation_name.strip_edges().is_empty():
|
|
505
|
-
return {"ok": false, "error": "Missing animationName"}
|
|
532
|
+
return { "ok": false, "error": "Missing animationName" }
|
|
506
533
|
if new_name.is_empty():
|
|
507
|
-
return {"ok": false, "error": "Missing newName"}
|
|
508
|
-
var library_name: String =
|
|
534
|
+
return { "ok": false, "error": "Missing newName" }
|
|
535
|
+
var library_name: String = (
|
|
536
|
+
animation_name.get_slice("/", 0) if animation_name.contains("/") else ""
|
|
537
|
+
)
|
|
509
538
|
var prefix: String = "" if library_name.is_empty() else library_name + "/"
|
|
510
539
|
if not prefix.is_empty() and new_name.begins_with(prefix):
|
|
511
540
|
new_name = new_name.trim_prefix(prefix)
|
|
@@ -515,15 +544,18 @@ func rename_animation(args: Dictionary) -> Dictionary:
|
|
|
515
544
|
"error":
|
|
516
545
|
(
|
|
517
546
|
(
|
|
518
|
-
"rename keeps an animation in its library (%s), so newName is its name there,
|
|
519
|
-
+ "no library before a slash."
|
|
547
|
+
"rename keeps an animation in its library (%s), so newName is its name there, "
|
|
548
|
+
+ "with no library before a slash."
|
|
520
549
|
)
|
|
521
550
|
% ("the default one" if library_name.is_empty() else library_name)
|
|
522
551
|
),
|
|
523
552
|
}
|
|
524
553
|
for forbidden: String in [":", ",", "["]:
|
|
525
554
|
if new_name.contains(forbidden):
|
|
526
|
-
return {
|
|
555
|
+
return {
|
|
556
|
+
"ok": false,
|
|
557
|
+
"error": "An animation's name cannot hold : , or [, and %s does." % new_name,
|
|
558
|
+
}
|
|
527
559
|
var new_full: String = prefix + new_name
|
|
528
560
|
|
|
529
561
|
var found: Array = _animation_at(scene_path, player_node_path, animation_name)
|
|
@@ -553,7 +585,7 @@ func rename_animation(args: Dictionary) -> Dictionary:
|
|
|
553
585
|
# As the editor's rename sets it: the library's key alone left the file's resource_name, which
|
|
554
586
|
# the inspector shows, on the old name.
|
|
555
587
|
player.get_animation(StringName(new_full)).resource_name = new_name
|
|
556
|
-
var answer: Dictionary = {"ok": true, "animationName": new_full, "was": animation_name}
|
|
588
|
+
var answer: Dictionary = { "ok": true, "animationName": new_full, "was": animation_name }
|
|
557
589
|
if autoplayed:
|
|
558
590
|
answer["autoplay"] = new_full
|
|
559
591
|
var followed: Array = []
|
|
@@ -581,9 +613,9 @@ func remove_animation(args: Dictionary) -> Dictionary:
|
|
|
581
613
|
var player_node_path: String = str(args.get("playerNodePath", "."))
|
|
582
614
|
var animation_name: String = str(args.get("animationName", ""))
|
|
583
615
|
if scene_path.strip_edges() == "res://":
|
|
584
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
616
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
585
617
|
if animation_name.strip_edges().is_empty():
|
|
586
|
-
return {"ok": false, "error": "Missing animationName"}
|
|
618
|
+
return { "ok": false, "error": "Missing animationName" }
|
|
587
619
|
|
|
588
620
|
var found: Array = _animation_at(scene_path, player_node_path, animation_name)
|
|
589
621
|
if found[1] == null:
|
|
@@ -594,12 +626,14 @@ func remove_animation(args: Dictionary) -> Dictionary:
|
|
|
594
626
|
var homes: Array = _homes(scene, [[library, "animation library"]])
|
|
595
627
|
if not homes[0]:
|
|
596
628
|
return scene.refuse(str(homes[1]))
|
|
597
|
-
var called: String =
|
|
629
|
+
var called: String = (
|
|
630
|
+
animation_name.get_slice("/", 1) if animation_name.contains("/") else animation_name
|
|
631
|
+
)
|
|
598
632
|
var autoplayed: bool = str(player.autoplay) == animation_name
|
|
599
633
|
library.remove_animation(StringName(called))
|
|
600
634
|
if player.has_animation(StringName(animation_name)):
|
|
601
635
|
return scene.refuse("The library did not let go of %s." % animation_name)
|
|
602
|
-
var answer: Dictionary = {"ok": true, "removed": animation_name}
|
|
636
|
+
var answer: Dictionary = { "ok": true, "removed": animation_name }
|
|
603
637
|
if autoplayed:
|
|
604
638
|
player.autoplay = ""
|
|
605
639
|
answer["autoplayCleared"] = true
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends "animation_tools.gd"
|
|
3
|
-
|
|
4
|
-
##
|
|
5
|
-
## whose helpers it shares.
|
|
3
|
+
## The tracks and keys of an animation, beside the players and libraries the script it extends
|
|
4
|
+
## edits, whose helpers it shares.
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
# =============================================================================
|
|
@@ -15,19 +14,19 @@ func add_animation_track(args: Dictionary) -> Dictionary:
|
|
|
15
14
|
var given_track: Variant = args.get("track", {})
|
|
16
15
|
|
|
17
16
|
if scene_path.strip_edges() == "res://":
|
|
18
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
17
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
19
18
|
if animation_name.strip_edges().is_empty():
|
|
20
|
-
return {"ok": false, "error": "Missing animationName"}
|
|
19
|
+
return { "ok": false, "error": "Missing animationName" }
|
|
21
20
|
var track: Dictionary = given_track if given_track is Dictionary else {}
|
|
22
21
|
if track.is_empty():
|
|
23
|
-
return {"ok": false, "error": "Missing track"}
|
|
24
|
-
# The schema leaves keyframes out of what track requires, since remove_track finds a track
|
|
25
|
-
# them.
|
|
22
|
+
return { "ok": false, "error": "Missing track" }
|
|
23
|
+
# The schema leaves keyframes out of what track requires, since remove_track finds a track
|
|
24
|
+
# without them.
|
|
26
25
|
if not track.has("keyframes"):
|
|
27
|
-
return {"ok": false, "error": "Missing track.keyframes"}
|
|
26
|
+
return { "ok": false, "error": "Missing track.keyframes" }
|
|
28
27
|
var keyed: Array = _keyframes(track)
|
|
29
28
|
if not keyed[0]:
|
|
30
|
-
return {"ok": false, "error": keyed[1]}
|
|
29
|
+
return { "ok": false, "error": keyed[1] }
|
|
31
30
|
|
|
32
31
|
# Asked of the player, which knows every library: looking only in the default one refused
|
|
33
32
|
# "moves/walk" as missing.
|
|
@@ -48,13 +47,16 @@ func add_animation_track(args: Dictionary) -> Dictionary:
|
|
|
48
47
|
target_path = "."
|
|
49
48
|
var target: Node = _animated(player, target_path)
|
|
50
49
|
if target == null:
|
|
51
|
-
return
|
|
52
|
-
|
|
50
|
+
return (
|
|
51
|
+
scene
|
|
52
|
+
. refuse(
|
|
53
53
|
(
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
(
|
|
55
|
+
"No node at %s from the player's root node, %s, which is what the track "
|
|
56
|
+
+ "path is read against."
|
|
57
|
+
)
|
|
58
|
+
% [target_path, str(player.root_node)]
|
|
56
59
|
)
|
|
57
|
-
% [target_path, str(player.root_node)]
|
|
58
60
|
)
|
|
59
61
|
)
|
|
60
62
|
var keyframes: Array = keyed[1]
|
|
@@ -120,7 +122,12 @@ func add_animation_track(args: Dictionary) -> Dictionary:
|
|
|
120
122
|
## animates: two tracks for one path both play, the later overriding the earlier, and a key meant
|
|
121
123
|
## to extend the motion was split off where reading the animation showed two halves.
|
|
122
124
|
func _add_track(
|
|
123
|
-
anim: Animation,
|
|
125
|
+
anim: Animation,
|
|
126
|
+
track: Dictionary,
|
|
127
|
+
target: Node,
|
|
128
|
+
target_path: String,
|
|
129
|
+
keyframes: Array,
|
|
130
|
+
replacing: bool
|
|
124
131
|
) -> Array:
|
|
125
132
|
var track_type: String = str(track.get("type", ""))
|
|
126
133
|
var keys: Array = []
|
|
@@ -148,7 +155,7 @@ func _add_track(
|
|
|
148
155
|
return _not_added("keyframes[%d].args must be a list." % index)
|
|
149
156
|
var raw: Array = given
|
|
150
157
|
var arguments: Array = _fitted(target, method_name, _parse_method_args(raw))
|
|
151
|
-
keys.append({"method": method_name, "args": arguments})
|
|
158
|
+
keys.append({ "method": method_name, "args": arguments })
|
|
152
159
|
if misfit.is_empty():
|
|
153
160
|
misfit = _key_misfit(target, method_name, arguments, index)
|
|
154
161
|
method_found = target.has_method(method_name)
|
|
@@ -156,7 +163,10 @@ func _add_track(
|
|
|
156
163
|
else:
|
|
157
164
|
return _not_added(
|
|
158
165
|
(
|
|
159
|
-
|
|
166
|
+
(
|
|
167
|
+
"add_track makes property and method tracks, and track.type is %s; "
|
|
168
|
+
+ "remove_track takes any."
|
|
169
|
+
)
|
|
160
170
|
% track_type
|
|
161
171
|
)
|
|
162
172
|
)
|
|
@@ -178,14 +188,16 @@ func _add_track(
|
|
|
178
188
|
return _not_added(
|
|
179
189
|
(
|
|
180
190
|
(
|
|
181
|
-
"Track %d already animates %s and has a key at %s s, which
|
|
182
|
-
+ "replace. Nothing was written; replaceKeys true
|
|
191
|
+
"Track %d already animates %s and has a key at %s s, which "
|
|
192
|
+
+ "keyframes[%d] would replace. Nothing was written; replaceKeys true "
|
|
193
|
+
+ "replaces it."
|
|
183
194
|
)
|
|
184
195
|
% [track_idx, str(path), _seconds(time), index]
|
|
185
196
|
)
|
|
186
197
|
)
|
|
187
|
-
# A method key's value is its call, {"method": ..., "args": [...]}. The time is as
|
|
188
|
-
# the key's own, read back from a scene's 32-bit times, puts 0.1 at
|
|
198
|
+
# A method key's value is its call, {"method": ..., "args": [...]}. The time is as
|
|
199
|
+
# asked: the key's own, read back from a scene's 32-bit times, puts 0.1 at
|
|
200
|
+
# 0.100000001490116.
|
|
189
201
|
(
|
|
190
202
|
replaced
|
|
191
203
|
. append(
|
|
@@ -228,7 +240,9 @@ static func _fitted(target: Node, method: String, arguments: Array) -> Array:
|
|
|
228
240
|
declared = GdhRead.as_int(parameter.get("type", TYPE_NIL))
|
|
229
241
|
if value is float and declared != TYPE_FLOAT:
|
|
230
242
|
var number: float = value
|
|
231
|
-
if is_finite(number)
|
|
243
|
+
if (is_finite(number)
|
|
244
|
+
and number == floorf(number)
|
|
245
|
+
and absf(number) < 9_007_199_254_740_992.0):
|
|
232
246
|
value = int(number)
|
|
233
247
|
fitted.append(value)
|
|
234
248
|
return fitted
|
|
@@ -256,10 +270,16 @@ static func _key_misfit(target: Node, method: String, arguments: Array, index: i
|
|
|
256
270
|
else ("1 argument" if arguments.size() == 1 else "%d arguments" % arguments.size())
|
|
257
271
|
)
|
|
258
272
|
if arguments.size() < needed:
|
|
259
|
-
return
|
|
273
|
+
return (
|
|
274
|
+
"keyframes[%d] calls %s with %s, and it needs %d%s"
|
|
275
|
+
% [index, method, passed, needed, fails]
|
|
276
|
+
)
|
|
260
277
|
if arguments.size() > parameters.size():
|
|
261
278
|
var most: String = "none" if parameters.is_empty() else "at most %d" % parameters.size()
|
|
262
|
-
return
|
|
279
|
+
return (
|
|
280
|
+
"keyframes[%d] calls %s with %s, and it takes %s%s"
|
|
281
|
+
% [index, method, passed, most, fails]
|
|
282
|
+
)
|
|
263
283
|
for at: int in arguments.size():
|
|
264
284
|
var parameter: Dictionary = parameters[at]
|
|
265
285
|
var taken_type: int = GdhRead.as_int(parameter.get("type", TYPE_NIL))
|
|
@@ -276,7 +296,7 @@ static func _key_misfit(target: Node, method: String, arguments: Array, index: i
|
|
|
276
296
|
method,
|
|
277
297
|
str(parameter.get("name", "")),
|
|
278
298
|
type_string(taken_type),
|
|
279
|
-
fails
|
|
299
|
+
fails,
|
|
280
300
|
]
|
|
281
301
|
)
|
|
282
302
|
return ""
|
|
@@ -293,22 +313,22 @@ func remove_animation_track(args: Dictionary) -> Dictionary:
|
|
|
293
313
|
var animation_name: String = str(args.get("animationName", ""))
|
|
294
314
|
var given_track: Variant = args.get("track", {})
|
|
295
315
|
if scene_path.strip_edges() == "res://":
|
|
296
|
-
return {"ok": false, "error": "Missing scenePath"}
|
|
316
|
+
return { "ok": false, "error": "Missing scenePath" }
|
|
297
317
|
if animation_name.strip_edges().is_empty():
|
|
298
|
-
return {"ok": false, "error": "Missing animationName"}
|
|
318
|
+
return { "ok": false, "error": "Missing animationName" }
|
|
299
319
|
var track: Dictionary = given_track if given_track is Dictionary else {}
|
|
300
320
|
if track.is_empty():
|
|
301
|
-
return {"ok": false, "error": "Missing track"}
|
|
321
|
+
return { "ok": false, "error": "Missing track" }
|
|
302
322
|
var track_type: String = str(track.get("type", ""))
|
|
303
323
|
if not track_type in GdhAnimationReads.TRACK_TYPE_NAMES.values():
|
|
304
|
-
return {"ok": false, "error": "Unsupported track.type: " + track_type}
|
|
324
|
+
return { "ok": false, "error": "Unsupported track.type: " + track_type }
|
|
305
325
|
var node_path: String = str(track.get("nodePath", ""))
|
|
306
326
|
var property: String = str(track.get("property", ""))
|
|
307
327
|
var shape: Array = [track_type, node_path, property]
|
|
308
328
|
var method: String = str(track.get("method", "")) if track_type == "method" else ""
|
|
309
329
|
var read_times: Variant = _times_of(args.get("times"))
|
|
310
330
|
if read_times is String:
|
|
311
|
-
return {"ok": false, "error": read_times}
|
|
331
|
+
return { "ok": false, "error": read_times }
|
|
312
332
|
var times: Array = read_times
|
|
313
333
|
|
|
314
334
|
var found: Array = _animation_at(scene_path, player_node_path, animation_name)
|
|
@@ -335,7 +355,7 @@ func remove_animation_track(args: Dictionary) -> Dictionary:
|
|
|
335
355
|
% [
|
|
336
356
|
animation_name,
|
|
337
357
|
track_type,
|
|
338
|
-
node_path if property.is_empty() else "%s:%s" % [node_path, property]
|
|
358
|
+
node_path if property.is_empty() else "%s:%s" % [node_path, property],
|
|
339
359
|
]
|
|
340
360
|
)
|
|
341
361
|
if held.is_empty():
|
|
@@ -353,7 +373,9 @@ func remove_animation_track(args: Dictionary) -> Dictionary:
|
|
|
353
373
|
var timed: Array = _keys_at(anim, calling, times)
|
|
354
374
|
if timed[1] != "":
|
|
355
375
|
var on: String = node_path if property.is_empty() else "%s:%s" % [node_path, property]
|
|
356
|
-
return scene.refuse(
|
|
376
|
+
return scene.refuse(
|
|
377
|
+
"%s's %s track on %s %s" % [animation_name, track_type, on, timed[1]]
|
|
378
|
+
)
|
|
357
379
|
calling = timed[0]
|
|
358
380
|
var keys: int = 0
|
|
359
381
|
var tracks_removed: int = 0
|
|
@@ -383,7 +405,7 @@ func remove_animation_track(args: Dictionary) -> Dictionary:
|
|
|
383
405
|
if loaded.get_track_count() != left or _key_total(loaded) != keys_left:
|
|
384
406
|
return "the track would still be in %s when the scene loads." % animation_name
|
|
385
407
|
return ""
|
|
386
|
-
var removed: Dictionary = {"type": track_type, "nodePath": node_path}
|
|
408
|
+
var removed: Dictionary = { "type": track_type, "nodePath": node_path }
|
|
387
409
|
if not property.is_empty():
|
|
388
410
|
removed["property"] = property
|
|
389
411
|
if not method.is_empty():
|
|
@@ -432,8 +454,8 @@ static func _times_of(given: Variant) -> Variant:
|
|
|
432
454
|
|
|
433
455
|
|
|
434
456
|
## Of the keys [param calling] names, as [method _keys_calling] answers them, the ones at
|
|
435
|
-
## [param times], as [[[track, key]...], ""], or with the second the words refusing a time none is
|
|
436
|
-
## Removing a key took removing its whole track, so moving one key in time rewrote every other.
|
|
457
|
+
## [param times], as [[[track, key]...], ""], or with the second the words refusing a time none is
|
|
458
|
+
## at. Removing a key took removing its whole track, so moving one key in time rewrote every other.
|
|
437
459
|
static func _keys_at(anim: Animation, calling: Array, times: Array) -> Array:
|
|
438
460
|
var at: Array = []
|
|
439
461
|
var held: PackedStringArray = []
|
|
@@ -457,7 +479,10 @@ static func _keys_at(anim: Animation, calling: Array, times: Array) -> Array:
|
|
|
457
479
|
var _named: bool = named.append(str(given))
|
|
458
480
|
return [
|
|
459
481
|
at,
|
|
460
|
-
|
|
482
|
+
(
|
|
483
|
+
"has no key at %s s; its keys are at %s s. Nothing was removed."
|
|
484
|
+
% [", ".join(named), ", ".join(held)]
|
|
485
|
+
),
|
|
461
486
|
]
|
|
462
487
|
|
|
463
488
|
|
|
@@ -483,7 +508,9 @@ static func _key_total(anim: Animation) -> int:
|
|
|
483
508
|
static func _track_shape(anim: Animation, index: int) -> Array:
|
|
484
509
|
var path: NodePath = anim.track_get_path(index)
|
|
485
510
|
return [
|
|
486
|
-
GdhAnimationReads.TRACK_TYPE_NAMES.get(
|
|
511
|
+
GdhAnimationReads.TRACK_TYPE_NAMES.get(
|
|
512
|
+
anim.track_get_type(index), str(anim.track_get_type(index))
|
|
513
|
+
),
|
|
487
514
|
str(path.get_concatenated_names()),
|
|
488
515
|
str(path.get_concatenated_subnames()),
|
|
489
516
|
]
|
|
@@ -508,7 +535,8 @@ func _keyframes(track: Dictionary) -> Array:
|
|
|
508
535
|
var seconds: float = GdhRead.as_float(time)
|
|
509
536
|
if times.has(seconds):
|
|
510
537
|
return [
|
|
511
|
-
false,
|
|
538
|
+
false,
|
|
539
|
+
"keyframes[%d] and [%d] are both at %s seconds." % [times[seconds], index, seconds],
|
|
512
540
|
]
|
|
513
541
|
times[seconds] = index
|
|
514
542
|
return [true, keyframes]
|
|
@@ -530,7 +558,9 @@ func _key_values(target: Node, prop_name: String, keyframes: Array) -> Array:
|
|
|
530
558
|
# A subname such as position:x keys one component, which is a number.
|
|
531
559
|
var component: Variant = _properties.parse(raw, TYPE_FLOAT)
|
|
532
560
|
if not component is float:
|
|
533
|
-
return [
|
|
561
|
+
return [
|
|
562
|
+
false, "keyframes[%d].value keys %s, which is a number." % [index, prop_name],
|
|
563
|
+
]
|
|
534
564
|
values.append(component)
|
|
535
565
|
continue
|
|
536
566
|
var value: Array = _properties.value_for(target, declared, raw)
|