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,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## What reaches the running game the way a keyboard reaches it: text typed into the field that has
|
|
4
3
|
## the focus, and an item chosen out of a menu the way a keyboard chooses one.
|
|
5
4
|
|
|
@@ -8,7 +7,6 @@ const GdhRead = preload("reading.gd")
|
|
|
8
7
|
const GdhTargets = preload("runtime_targets.gd")
|
|
9
8
|
const GdhValues = preload("runtime_values.gd")
|
|
10
9
|
const GdhWords = preload("runtime_words.gd")
|
|
11
|
-
|
|
12
10
|
## The two characters a field reads as keys rather than as text.
|
|
13
11
|
const NEWLINE: int = 10
|
|
14
12
|
const TAB: int = 9
|
|
@@ -43,13 +41,15 @@ func inject_text(params: Dictionary) -> Dictionary:
|
|
|
43
41
|
var text: String = str(params.get("text", ""))
|
|
44
42
|
var over: bool = GdhRead.as_bool(params.get("replace", false))
|
|
45
43
|
if text.is_empty() and not over:
|
|
46
|
-
return {
|
|
44
|
+
return {
|
|
45
|
+
"type": "error", "message": "text needs something to type, or replace to empty a field",
|
|
46
|
+
}
|
|
47
47
|
|
|
48
48
|
var viewport: Viewport = _host.get_tree().root
|
|
49
49
|
var focused: Control = focus_owner(viewport)
|
|
50
50
|
var shut: String = _shut_to_typing(focused)
|
|
51
51
|
if not shut.is_empty():
|
|
52
|
-
return {"type": "error", "message": shut}
|
|
52
|
+
return { "type": "error", "message": shut }
|
|
53
53
|
|
|
54
54
|
# Where it goes, which is the one thing a caller cannot see from here, read before the keys: a
|
|
55
55
|
# name field that swaps its panel out on Enter has left the tree by the time the keys are done,
|
|
@@ -87,9 +87,10 @@ func inject_text(params: Dictionary) -> Dictionary:
|
|
|
87
87
|
if into != null and before.is_empty():
|
|
88
88
|
answer["note"] = (
|
|
89
89
|
(
|
|
90
|
-
"%s has the focus and is no LineEdit or TextEdit, so each character reached it as
|
|
91
|
-
+ " press and went on to the game's input unless it kept it, and a focused
|
|
92
|
-
+ " space or Enter as a press of it. Click a field first to type
|
|
90
|
+
"%s has the focus and is no LineEdit or TextEdit, so each character reached it as "
|
|
91
|
+
+ "a key press and went on to the game's input unless it kept it, and a focused "
|
|
92
|
+
+ "button takes a space or Enter as a press of it. Click a field first to type "
|
|
93
|
+
+ "into one."
|
|
93
94
|
)
|
|
94
95
|
% into
|
|
95
96
|
)
|
|
@@ -102,10 +103,10 @@ func inject_text(params: Dictionary) -> Dictionary:
|
|
|
102
103
|
static func field_before(focused: Control) -> Dictionary:
|
|
103
104
|
var field: LineEdit = focused as LineEdit
|
|
104
105
|
if field != null:
|
|
105
|
-
return {"text": field.text, "editing": field.is_editing()}
|
|
106
|
+
return { "text": field.text, "editing": field.is_editing() }
|
|
106
107
|
var box: TextEdit = focused as TextEdit
|
|
107
108
|
if box != null:
|
|
108
|
-
return {"text": box.text, "editing": true}
|
|
109
|
+
return { "text": box.text, "editing": true }
|
|
109
110
|
return {}
|
|
110
111
|
|
|
111
112
|
|
|
@@ -216,7 +217,9 @@ static func _shut_to_typing(focused: Control) -> String:
|
|
|
216
217
|
var field: LineEdit = focused as LineEdit
|
|
217
218
|
var box: TextEdit = focused as TextEdit
|
|
218
219
|
if (field != null and not field.editable) or (box != null and not box.editable):
|
|
219
|
-
return
|
|
220
|
+
return (
|
|
221
|
+
"%s has the focus and is read-only, so nothing typed lands in it." % focused.get_path()
|
|
222
|
+
)
|
|
220
223
|
if field == null or field.is_editing():
|
|
221
224
|
return ""
|
|
222
225
|
return (
|
|
@@ -281,7 +284,7 @@ static func _typed(glyph: int) -> InputEventKey:
|
|
|
281
284
|
func choose(params: Dictionary) -> Dictionary:
|
|
282
285
|
var node_path: String = str(params.get("path", ""))
|
|
283
286
|
if node_path.is_empty():
|
|
284
|
-
return {"type": "error", "message": "Node path required"}
|
|
287
|
+
return { "type": "error", "message": "Node path required" }
|
|
285
288
|
var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
|
|
286
289
|
if standing.has("message"):
|
|
287
290
|
return standing
|
|
@@ -295,7 +298,7 @@ func choose(params: Dictionary) -> Dictionary:
|
|
|
295
298
|
(
|
|
296
299
|
"%s is a %s, which is neither a PopupMenu nor something holding one"
|
|
297
300
|
% [node_path, node.get_class()]
|
|
298
|
-
)
|
|
301
|
+
),
|
|
299
302
|
}
|
|
300
303
|
|
|
301
304
|
if not params.has("index") and str(params.get("text", "")).is_empty():
|
|
@@ -305,27 +308,33 @@ func choose(params: Dictionary) -> Dictionary:
|
|
|
305
308
|
(
|
|
306
309
|
"%s needs the item named, by text or index. It holds: %s"
|
|
307
310
|
% [node_path, ", ".join(GdhMenus.items_of(menu))]
|
|
308
|
-
)
|
|
311
|
+
),
|
|
309
312
|
}
|
|
310
313
|
|
|
311
|
-
# A button a player cannot use is refused, as a click refuses one: the engine opens the menu of
|
|
312
|
-
# disabled or hidden OptionButton when asked to, measured on 4.7.2, and the pick then changed
|
|
313
|
-
# selection no player could have changed.
|
|
314
|
+
# A button a player cannot use is refused, as a click refuses one: the engine opens the menu of
|
|
315
|
+
# a disabled or hidden OptionButton when asked to, measured on 4.7.2, and the pick then changed
|
|
316
|
+
# a selection no player could have changed.
|
|
314
317
|
var holder: BaseButton = node as BaseButton
|
|
315
318
|
if holder != null and holder.disabled:
|
|
316
|
-
return {
|
|
319
|
+
return {
|
|
320
|
+
"type": "error", "message": "%s is disabled, so its menu cannot be opened" % node_path,
|
|
321
|
+
}
|
|
317
322
|
if holder != null and not holder.is_visible_in_tree():
|
|
318
323
|
return {
|
|
319
324
|
"type": "error",
|
|
320
325
|
"message":
|
|
321
|
-
|
|
326
|
+
(
|
|
327
|
+
"%s is not visible%s, so its menu cannot be opened"
|
|
328
|
+
% [node_path, GdhTargets.why_hidden(holder)]
|
|
329
|
+
),
|
|
322
330
|
}
|
|
323
331
|
|
|
324
332
|
# Shown first, because a menu nobody has opened has no focus to move and the press would go to
|
|
325
333
|
# whatever is behind it, and before the item is looked for, because opening is when a game fills
|
|
326
334
|
# a menu: one rebuilt on about_to_popup was read as it stood before, measured on 4.7.2, and the
|
|
327
|
-
# index found in the old items chose another item of the new ones. An OptionButton opens its
|
|
328
|
-
# a bare PopupMenu is popped where it already sits, which leaves a menu already open where
|
|
335
|
+
# index found in the old items chose another item of the new ones. An OptionButton opens its
|
|
336
|
+
# own; a bare PopupMenu is popped where it already sits, which leaves a menu already open where
|
|
337
|
+
# it is.
|
|
329
338
|
var opened: bool = GdhMenus.open_the_menu(node, menu)
|
|
330
339
|
await _host.get_tree().process_frame
|
|
331
340
|
|
|
@@ -339,15 +348,23 @@ func choose(params: Dictionary) -> Dictionary:
|
|
|
339
348
|
"type": "error",
|
|
340
349
|
"message":
|
|
341
350
|
(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
351
|
+
(
|
|
352
|
+
"%s has %d items that say %s, so nothing was chosen: %s. Name one by its whole "
|
|
353
|
+
+ "text or by index"
|
|
354
|
+
)
|
|
355
|
+
% [
|
|
356
|
+
node_path,
|
|
357
|
+
found.size(),
|
|
358
|
+
JSON.stringify(str(params.get("text", ""))),
|
|
359
|
+
", ".join(tied),
|
|
360
|
+
]
|
|
361
|
+
),
|
|
345
362
|
}
|
|
346
363
|
var index: int = found[0] if found.size() == 1 else -1
|
|
347
364
|
var refused: String = _not_a_choice(node_path, menu, index)
|
|
348
365
|
if not refused.is_empty():
|
|
349
366
|
await _shut(menu, opened)
|
|
350
|
-
return {"type": "error", "message": refused}
|
|
367
|
+
return { "type": "error", "message": refused }
|
|
351
368
|
|
|
352
369
|
# Read before the press, because the press can take the menu away: a game that rebuilds its
|
|
353
370
|
# settings screen on item_selected frees the button and its menu inside the pick, and reading
|
|
@@ -385,11 +402,11 @@ func choose(params: Dictionary) -> Dictionary:
|
|
|
385
402
|
"message":
|
|
386
403
|
(
|
|
387
404
|
(
|
|
388
|
-
"%s item %d, %s, had the focus and ui_accept was pressed, and the menu did not
|
|
389
|
-
+ " so nothing was chosen"
|
|
405
|
+
"%s item %d, %s, had the focus and ui_accept was pressed, and the menu did not "
|
|
406
|
+
+ "take it, so nothing was chosen"
|
|
390
407
|
)
|
|
391
408
|
% [node_path, index, answer["text"]]
|
|
392
|
-
)
|
|
409
|
+
),
|
|
393
410
|
}
|
|
394
411
|
|
|
395
412
|
answer["control_afterwards"] = GdhValues.afterwards(node)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## What crosses the wire between the game and the server, in both directions: Godot values
|
|
4
3
|
## as JSON-safe dictionaries, and JSON back into the values a property or parameter wants.
|
|
5
4
|
##
|
|
@@ -12,7 +11,6 @@ const GdhDeclared = preload("declared_values.gd")
|
|
|
12
11
|
const GdhRead = preload("reading.gd")
|
|
13
12
|
const GdhSerialisation = preload("serialisation.gd")
|
|
14
13
|
const GdhShapes = preload("value_shapes.gd")
|
|
15
|
-
|
|
16
14
|
## The engine singletons a call, a write or a read may name instead of a node: what the engine
|
|
17
15
|
## measures, and the engine settings that are the game's own. Not every singleton: OS runs programs
|
|
18
16
|
## and opens files, DisplayServer switches the keyboard's layout and Input moves the real pointer,
|
|
@@ -61,7 +59,7 @@ func deserialize(value: Variant) -> Variant:
|
|
|
61
59
|
## that take them are the property and the method.
|
|
62
60
|
static func node_at(root: Node, path: String) -> Dictionary:
|
|
63
61
|
if path.is_empty():
|
|
64
|
-
return {"type": "error", "message": "Node path required"}
|
|
62
|
+
return { "type": "error", "message": "Node path required" }
|
|
65
63
|
|
|
66
64
|
if path.contains(":"):
|
|
67
65
|
var shape: String = (
|
|
@@ -75,8 +73,8 @@ static func node_at(root: Node, path: String) -> Dictionary:
|
|
|
75
73
|
|
|
76
74
|
var node: Node = root.get_node_or_null(path)
|
|
77
75
|
if node == null:
|
|
78
|
-
return {"type": "error", "message": _not_found(root, path)}
|
|
79
|
-
return {"node": node}
|
|
76
|
+
return { "type": "error", "message": _not_found(root, path) }
|
|
77
|
+
return { "node": node }
|
|
80
78
|
|
|
81
79
|
|
|
82
80
|
## "Node not found" for [param path], with the deepest node of it the game has and the children that
|
|
@@ -90,7 +88,10 @@ static func _not_found(root: Node, path: String) -> String:
|
|
|
90
88
|
var meant: Node = scene.get_node_or_null(path)
|
|
91
89
|
if meant != null:
|
|
92
90
|
return (
|
|
93
|
-
|
|
91
|
+
(
|
|
92
|
+
"%s. Paths here go from the root, /root, not from the current scene, so this "
|
|
93
|
+
+ "one is %s."
|
|
94
|
+
)
|
|
94
95
|
% [said, str(meant.get_path())]
|
|
95
96
|
)
|
|
96
97
|
var parts: PackedStringArray = path.split("/", false)
|
|
@@ -111,7 +112,9 @@ static func _not_found(root: Node, path: String) -> String:
|
|
|
111
112
|
var names: PackedStringArray = []
|
|
112
113
|
for child: Node in at.get_children():
|
|
113
114
|
var _named: bool = names.append(str(child.name))
|
|
114
|
-
return GdhRead.missing_below(
|
|
115
|
+
return GdhRead.missing_below(
|
|
116
|
+
said, "", PackedStringArray([str(at.get_path())]), parts[step], names
|
|
117
|
+
)
|
|
115
118
|
|
|
116
119
|
|
|
117
120
|
## How many process frames a whole press holds before its release, from `holdFrames`: one unless
|
|
@@ -150,7 +153,7 @@ static func hold(tree: SceneTree, frames: int) -> Dictionary:
|
|
|
150
153
|
for _frame: int in frames:
|
|
151
154
|
await tree.process_frame
|
|
152
155
|
return {
|
|
153
|
-
"seconds": snappedf((Time.get_ticks_usec() - began_usec) /
|
|
156
|
+
"seconds": snappedf((Time.get_ticks_usec() - began_usec) / 1_000_000.0, 0.001),
|
|
154
157
|
"physics_frames": Engine.get_physics_frames() - began_tick,
|
|
155
158
|
}
|
|
156
159
|
|
|
@@ -169,9 +172,9 @@ static func glyph_of(keycode: Key, shifted: bool) -> int:
|
|
|
169
172
|
return keycode
|
|
170
173
|
|
|
171
174
|
|
|
172
|
-
## The refusal for [param name], which no key is called, with the key names nearest it. Read from
|
|
173
|
-
## engine's own names, printable keys and the special ones above [constant KEY_SPECIAL], since
|
|
174
|
-
## game takes any name OS.find_keycode_from_string does.
|
|
175
|
+
## The refusal for [param name], which no key is called, with the key names nearest it. Read from
|
|
176
|
+
## the engine's own names, printable keys and the special ones above [constant KEY_SPECIAL], since
|
|
177
|
+
## the game takes any name OS.find_keycode_from_string does.
|
|
175
178
|
static func unknown_key(name: String) -> String:
|
|
176
179
|
var close: PackedStringArray = GdhRead.nearest(name, _key_names())
|
|
177
180
|
var said: String = "No key is called %s." % name
|
|
@@ -205,8 +208,9 @@ static func _key_names() -> PackedStringArray:
|
|
|
205
208
|
|
|
206
209
|
|
|
207
210
|
## The refusal for [param action], which the input map lacks, with the nearest of the project's own
|
|
208
|
-
## actions, or else all of them. The engine's ui_ ones are in every project, and offered for any
|
|
209
|
-
## they only buried the project's ("dance" is near ui_cancel), so they are offered for a ui_
|
|
211
|
+
## actions, or else all of them. The engine's ui_ ones are in every project, and offered for any
|
|
212
|
+
## name they only buried the project's ("dance" is near ui_cancel), so they are offered for a ui_
|
|
213
|
+
## name only.
|
|
210
214
|
static func no_action(action: String) -> String:
|
|
211
215
|
var engine: PackedStringArray = []
|
|
212
216
|
var own: PackedStringArray = []
|
|
@@ -217,12 +221,23 @@ static func no_action(action: String) -> String:
|
|
|
217
221
|
var _owned: bool = own.append(str(named))
|
|
218
222
|
own.sort()
|
|
219
223
|
var said: String = "Action not found: %s" % action
|
|
220
|
-
var close: PackedStringArray = GdhRead.nearest(
|
|
224
|
+
var close: PackedStringArray = GdhRead.nearest(
|
|
225
|
+
action, engine if action.begins_with("ui_") else own
|
|
226
|
+
)
|
|
221
227
|
if not close.is_empty():
|
|
222
|
-
return
|
|
228
|
+
return (
|
|
229
|
+
"%s; the nearest %s %s."
|
|
230
|
+
% [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
231
|
+
)
|
|
223
232
|
if own.is_empty():
|
|
224
|
-
return
|
|
225
|
-
|
|
233
|
+
return (
|
|
234
|
+
"%s; the project defines no actions of its own, and the engine's are the ui_ ones."
|
|
235
|
+
% said
|
|
236
|
+
)
|
|
237
|
+
return (
|
|
238
|
+
"%s; the project's own actions are %s, beside the engine's ui_ ones."
|
|
239
|
+
% [said, ", ".join(own)]
|
|
240
|
+
)
|
|
226
241
|
|
|
227
242
|
|
|
228
243
|
## The actions of the input map that [param event] stands for, as Input reads it for
|
|
@@ -248,10 +263,11 @@ static func focus_path(host: Node) -> String:
|
|
|
248
263
|
return "" if holding == null else str(holding.get_path())
|
|
249
264
|
|
|
250
265
|
|
|
251
|
-
## [param said] with [param focused], the Control that had the focus, under focused. A key, an
|
|
252
|
-
## or a pad's event reaches that Control before the game's _unhandled_input: Space on a
|
|
253
|
-
## pressed it and the game's own handler counted the key as well, and a stick pushed
|
|
254
|
-
## focused list's selection and never reached the game, with nothing in the answer
|
|
266
|
+
## [param said] with [param focused], the Control that had the focus, under focused. A key, an
|
|
267
|
+
## action or a pad's event reaches that Control before the game's _unhandled_input: Space on a
|
|
268
|
+
## focused Button pressed it and the game's own handler counted the key as well, and a stick pushed
|
|
269
|
+
## down moved a focused list's selection and never reached the game, with nothing in the answer
|
|
270
|
+
## saying why.
|
|
255
271
|
##
|
|
256
272
|
## Where the focus is once the event has been handled goes under focused_after when it moved, null
|
|
257
273
|
## when nothing holds it then: ui_down on a focused button hands the focus to its neighbour, and an
|
|
@@ -314,7 +330,7 @@ static func press_shortcuts(root: Node, action: String) -> Dictionary:
|
|
|
314
330
|
var pressed: Variant = null
|
|
315
331
|
if not taken.is_empty():
|
|
316
332
|
pressed = taken[0]
|
|
317
|
-
return {"shortcuts": paths, "shortcut_pressed": pressed}
|
|
333
|
+
return { "shortcuts": paths, "shortcut_pressed": pressed }
|
|
318
334
|
|
|
319
335
|
|
|
320
336
|
## The methods [param object] answers to, as the engine lists them: for a script itself, the static
|
|
@@ -336,16 +352,16 @@ static func methods_of(object: Object) -> Array[Dictionary]:
|
|
|
336
352
|
## named in a value are found from, which for a singleton is [param root].
|
|
337
353
|
static func target_at(root: Node, path: String) -> Dictionary:
|
|
338
354
|
if SINGLETONS.has(path):
|
|
339
|
-
return {"object": Engine.get_singleton(path), "node": root}
|
|
355
|
+
return { "object": Engine.get_singleton(path), "node": root }
|
|
340
356
|
if not path.contains("/") and Engine.has_singleton(path):
|
|
341
357
|
return {
|
|
342
358
|
"type": "error",
|
|
343
359
|
"message":
|
|
344
360
|
(
|
|
345
361
|
(
|
|
346
|
-
"%s is an engine singleton, and the ones that can be reached are %s. The rest
|
|
347
|
-
+ " on the machine rather than the game: OS runs programs, DisplayServer
|
|
348
|
-
+ " keyboard's layout and Input moves the real pointer."
|
|
362
|
+
"%s is an engine singleton, and the ones that can be reached are %s. The rest "
|
|
363
|
+
+ "act on the machine rather than the game: OS runs programs, DisplayServer "
|
|
364
|
+
+ "switches the keyboard's layout and Input moves the real pointer."
|
|
349
365
|
)
|
|
350
366
|
% [path, ", ".join(SINGLETONS)]
|
|
351
367
|
),
|
|
@@ -353,7 +369,7 @@ static func target_at(root: Node, path: String) -> Dictionary:
|
|
|
353
369
|
var standing: Dictionary = node_at(root, path)
|
|
354
370
|
if standing.has("message"):
|
|
355
371
|
return standing
|
|
356
|
-
return {"object": standing["node"], "node": standing["node"]}
|
|
372
|
+
return { "object": standing["node"], "node": standing["node"] }
|
|
357
373
|
|
|
358
374
|
|
|
359
375
|
## The script [param path] names for a call of one of its static functions, as [method target_at]
|
|
@@ -379,7 +395,7 @@ static func class_at(root: Node, path: String) -> Dictionary:
|
|
|
379
395
|
"type": "error",
|
|
380
396
|
"message": "No script is at %s to call a static function of." % script_path,
|
|
381
397
|
}
|
|
382
|
-
return {"object": loaded, "node": root}
|
|
398
|
+
return { "object": loaded, "node": root }
|
|
383
399
|
|
|
384
400
|
|
|
385
401
|
## Whether two values can be compared without the comparison itself failing.
|
|
@@ -424,7 +440,8 @@ static func inexact(given: Variant, type: int) -> String:
|
|
|
424
440
|
return GdhSerialisation.inexact(given, type)
|
|
425
441
|
|
|
426
442
|
|
|
427
|
-
## What a refusal of [param value] where a [param type] goes ends with: see
|
|
443
|
+
## What a refusal of [param value] where a [param type] goes ends with: see
|
|
444
|
+
## [method GdhShapes.remedy].
|
|
428
445
|
static func remedy(value: Variant, type: int) -> String:
|
|
429
446
|
return GdhShapes.remedy(value, type)
|
|
430
447
|
|
|
@@ -444,8 +461,8 @@ func parameter_type(object: Object, method: String, index: int) -> int:
|
|
|
444
461
|
|
|
445
462
|
|
|
446
463
|
## The values of the enum argument [param index] of [param method] declares, by name, as
|
|
447
|
-
## `{"enum": "Mood", "values": {...}}`, or {} when it declares none. The engine gives such an
|
|
448
|
-
## the type int and the enum's name, as res://main.gd.Mood or Node.ProcessMode.
|
|
464
|
+
## `{"enum": "Mood", "values": {...}}`, or {} when it declares none. The engine gives such an
|
|
465
|
+
## argument the type int and the enum's name, as res://main.gd.Mood or Node.ProcessMode.
|
|
449
466
|
func parameter_enum(object: Object, method: String, index: int) -> Dictionary:
|
|
450
467
|
for entry: Dictionary in methods_of(object):
|
|
451
468
|
if entry.get("name", "") != method:
|
|
@@ -463,12 +480,12 @@ func parameter_enum(object: Object, method: String, index: int) -> Dictionary:
|
|
|
463
480
|
values = _enum_along(object if object is Script else object.get_script(), enum_name)
|
|
464
481
|
if values.is_empty():
|
|
465
482
|
return {}
|
|
466
|
-
return {"enum": enum_name, "values": values}
|
|
483
|
+
return { "enum": enum_name, "values": values }
|
|
467
484
|
return {}
|
|
468
485
|
|
|
469
486
|
|
|
470
|
-
## The enum [param named] declared by [param script] or a script it extends, or {}. A script built
|
|
471
|
-
## a scene or made at run time names its enums by an id nothing loads, gdscript://-92233….gd.Mood
|
|
487
|
+
## The enum [param named] declared by [param script] or a script it extends, or {}. A script built
|
|
488
|
+
## in a scene or made at run time names its enums by an id nothing loads, gdscript://-92233….gd.Mood
|
|
472
489
|
## measured on 4.7.2, and a method of the object called declares its enums along this chain.
|
|
473
490
|
static func _enum_along(script: Variant, named: String) -> Dictionary:
|
|
474
491
|
if not script is Script:
|
|
@@ -506,12 +523,12 @@ func parameter_class(object: Object, method: String, index: int) -> String:
|
|
|
506
523
|
return ""
|
|
507
524
|
|
|
508
525
|
|
|
509
|
-
## What the slot [param named] on [param holder] declares it holds, as
|
|
510
|
-
## String, "script": Script or null, "declared": Dictionary}`: a property's
|
|
511
|
-
## element type of a typed list or the value type of a typed map. TYPE_NIL when
|
|
512
|
-
## which is also what a property declared as Variant or left untyped reads as:
|
|
513
|
-
## anything. declared is the property's entry in the property list, which
|
|
514
|
-
## range, and empty for anything that is not a property.
|
|
526
|
+
## What the slot [param named] on [param holder] declares it holds, as
|
|
527
|
+
## `{"type": int, "class": String, "script": Script or null, "declared": Dictionary}`: a property's
|
|
528
|
+
## declaration, or the element type of a typed list or the value type of a typed map. TYPE_NIL when
|
|
529
|
+
## nothing is declared, which is also what a property declared as Variant or left untyped reads as:
|
|
530
|
+
## either way it takes anything. declared is the property's entry in the property list, which
|
|
531
|
+
## carries its enum or range, and empty for anything that is not a property.
|
|
515
532
|
##
|
|
516
533
|
## Asked rather than read off the value in the slot, because an object slot can be empty: a
|
|
517
534
|
## property typed `Gear` holding null reads as TYPE_NIL, and anything written into it passed the
|
|
@@ -525,7 +542,7 @@ static func slot_declared(holder: Variant, named: String) -> Dictionary:
|
|
|
525
542
|
# form's an integer, a rectangle's size a vector of the same form.
|
|
526
543
|
if named in GdhSerialisation.components_of(holder):
|
|
527
544
|
var component: Variant = holder[named]
|
|
528
|
-
return {"type": typeof(component), "class": "", "script": null, "declared": {}}
|
|
545
|
+
return { "type": typeof(component), "class": "", "script": null, "declared": {} }
|
|
529
546
|
if holder is Array:
|
|
530
547
|
var items: Array = holder
|
|
531
548
|
if items.is_typed():
|
|
@@ -540,7 +557,8 @@ static func slot_declared(holder: Variant, named: String) -> Dictionary:
|
|
|
540
557
|
if map.is_typed_value():
|
|
541
558
|
return {
|
|
542
559
|
"type": map.get_typed_value_builtin(),
|
|
543
|
-
"class":
|
|
560
|
+
"class":
|
|
561
|
+
_typed_class(map.get_typed_value_script(), map.get_typed_value_class_name()),
|
|
544
562
|
"script": _script_or_null(map.get_typed_value_script()),
|
|
545
563
|
"declared": {},
|
|
546
564
|
}
|
|
@@ -554,7 +572,7 @@ static func slot_declared(holder: Variant, named: String) -> Dictionary:
|
|
|
554
572
|
"script": null,
|
|
555
573
|
"declared": entry,
|
|
556
574
|
}
|
|
557
|
-
return {"type": TYPE_NIL, "class": "", "script": null, "declared": {}}
|
|
575
|
+
return { "type": TYPE_NIL, "class": "", "script": null, "declared": {} }
|
|
558
576
|
|
|
559
577
|
|
|
560
578
|
static func _script_or_null(typed: Variant) -> Script:
|
|
@@ -574,10 +592,10 @@ static func _typed_class(script: Variant, engine_class: StringName) -> String:
|
|
|
574
592
|
## Whether [param object] is a [param declared]: an engine class, or a script class by its global
|
|
575
593
|
## name anywhere along the script's inheritance.
|
|
576
594
|
##
|
|
577
|
-
## Against [param declared_script] itself when there is one. A script with no global name is named
|
|
578
|
-
## the engine class it extends, so an object of that class with another script, or none, passes
|
|
579
|
-
## name, and a typed list built with it drops the element: measured on 4.7.2, an Array[Walker]
|
|
580
|
-
## a plain Node3D comes out empty, the engine's errors going to the game's output alone.
|
|
595
|
+
## Against [param declared_script] itself when there is one. A script with no global name is named
|
|
596
|
+
## by the engine class it extends, so an object of that class with another script, or none, passes
|
|
597
|
+
## the name, and a typed list built with it drops the element: measured on 4.7.2, an Array[Walker]
|
|
598
|
+
## given a plain Node3D comes out empty, the engine's errors going to the game's output alone.
|
|
581
599
|
static func is_a(object: Object, declared: String, declared_script: Script = null) -> bool:
|
|
582
600
|
if declared_script != null:
|
|
583
601
|
var own: Variant = object.get_script()
|
|
@@ -667,7 +685,13 @@ func parameter_container(object: Object, method: String, index: int) -> Variant:
|
|
|
667
685
|
var value_builtin: int = entry_type["builtin"]
|
|
668
686
|
var value_class: StringName = entry_type["class"]
|
|
669
687
|
return Dictionary(
|
|
670
|
-
{},
|
|
688
|
+
{},
|
|
689
|
+
key_builtin,
|
|
690
|
+
key_class,
|
|
691
|
+
key["script"],
|
|
692
|
+
value_builtin,
|
|
693
|
+
value_class,
|
|
694
|
+
entry_type["script"]
|
|
671
695
|
)
|
|
672
696
|
return null
|
|
673
697
|
return null
|
|
@@ -679,15 +703,17 @@ func parameter_container(object: Object, method: String, index: int) -> Variant:
|
|
|
679
703
|
static func _element_type(named: String) -> Dictionary:
|
|
680
704
|
for type: int in TYPE_MAX:
|
|
681
705
|
if type != TYPE_OBJECT and type_string(type) == named:
|
|
682
|
-
return {"builtin": type, "class": &"", "script": null}
|
|
706
|
+
return { "builtin": type, "class": &"", "script": null }
|
|
683
707
|
if ClassDB.class_exists(named):
|
|
684
|
-
return {"builtin": TYPE_OBJECT, "class": StringName(named), "script": null}
|
|
708
|
+
return { "builtin": TYPE_OBJECT, "class": StringName(named), "script": null }
|
|
685
709
|
for global: Dictionary in ProjectSettings.get_global_class_list():
|
|
686
710
|
if str(global.get("class", "")) == named:
|
|
687
711
|
var script: Script = load(str(global.get("path", "")))
|
|
688
712
|
if script == null:
|
|
689
713
|
return {}
|
|
690
|
-
return {
|
|
714
|
+
return {
|
|
715
|
+
"builtin": TYPE_OBJECT, "class": script.get_instance_base_type(), "script": script,
|
|
716
|
+
}
|
|
691
717
|
return {}
|
|
692
718
|
|
|
693
719
|
|
|
@@ -708,7 +734,7 @@ func typed_like(given: Variant, like: Variant, resolve: Callable) -> Dictionary:
|
|
|
708
734
|
if like is Array and given is Array:
|
|
709
735
|
var template: Array = like
|
|
710
736
|
if not template.is_typed():
|
|
711
|
-
return {"value": given}
|
|
737
|
+
return { "value": given }
|
|
712
738
|
var items: Array = given
|
|
713
739
|
var built: Array = []
|
|
714
740
|
for index: int in items.size():
|
|
@@ -720,18 +746,21 @@ func typed_like(given: Variant, like: Variant, resolve: Callable) -> Dictionary:
|
|
|
720
746
|
resolve
|
|
721
747
|
)
|
|
722
748
|
if element.has("message"):
|
|
723
|
-
return {"message": "element %d %s" % [index, element["message"]]}
|
|
749
|
+
return { "message": "element %d %s" % [index, element["message"]] }
|
|
724
750
|
built.append(element["value"])
|
|
725
751
|
var typed: Array = Array(
|
|
726
|
-
built,
|
|
752
|
+
built,
|
|
753
|
+
template.get_typed_builtin(),
|
|
754
|
+
template.get_typed_class_name(),
|
|
755
|
+
template.get_typed_script()
|
|
727
756
|
)
|
|
728
757
|
if typed.size() != built.size():
|
|
729
|
-
return {"message": "the engine would not take its elements as what the list holds."}
|
|
730
|
-
return {"value": typed}
|
|
758
|
+
return { "message": "the engine would not take its elements as what the list holds." }
|
|
759
|
+
return { "value": typed }
|
|
731
760
|
if like is Dictionary and given is Dictionary:
|
|
732
761
|
var map_like: Dictionary = like
|
|
733
762
|
if not map_like.is_typed():
|
|
734
|
-
return {"value": given}
|
|
763
|
+
return { "value": given }
|
|
735
764
|
var entries: Dictionary = given
|
|
736
765
|
var rebuilt: Dictionary = {}
|
|
737
766
|
for key: Variant in entries:
|
|
@@ -743,16 +772,18 @@ func typed_like(given: Variant, like: Variant, resolve: Callable) -> Dictionary:
|
|
|
743
772
|
resolve
|
|
744
773
|
)
|
|
745
774
|
if key_fitted.has("message"):
|
|
746
|
-
return {"message": "key %s %s" % [str(key), key_fitted["message"]]}
|
|
775
|
+
return { "message": "key %s %s" % [str(key), key_fitted["message"]] }
|
|
747
776
|
var value_fitted: Dictionary = _element(
|
|
748
777
|
entries[key],
|
|
749
778
|
map_like.get_typed_value_builtin(),
|
|
750
|
-
_typed_class(
|
|
779
|
+
_typed_class(
|
|
780
|
+
map_like.get_typed_value_script(), map_like.get_typed_value_class_name()
|
|
781
|
+
),
|
|
751
782
|
_script_or_null(map_like.get_typed_value_script()),
|
|
752
783
|
resolve
|
|
753
784
|
)
|
|
754
785
|
if value_fitted.has("message"):
|
|
755
|
-
return {"message": "the value under %s %s" % [str(key), value_fitted["message"]]}
|
|
786
|
+
return { "message": "the value under %s %s" % [str(key), value_fitted["message"]] }
|
|
756
787
|
rebuilt[key_fitted["value"]] = value_fitted["value"]
|
|
757
788
|
var typed_map: Dictionary = Dictionary(
|
|
758
789
|
rebuilt,
|
|
@@ -764,9 +795,9 @@ func typed_like(given: Variant, like: Variant, resolve: Callable) -> Dictionary:
|
|
|
764
795
|
map_like.get_typed_value_script()
|
|
765
796
|
)
|
|
766
797
|
if typed_map.size() != rebuilt.size():
|
|
767
|
-
return {"message": "the engine would not take its entries as what the map holds."}
|
|
768
|
-
return {"value": typed_map}
|
|
769
|
-
return {"value": given}
|
|
798
|
+
return { "message": "the engine would not take its entries as what the map holds." }
|
|
799
|
+
return { "value": typed_map }
|
|
800
|
+
return { "value": given }
|
|
770
801
|
|
|
771
802
|
|
|
772
803
|
## One element fitted to a container's element type, as `{"value"}` or `{"message"}` saying what it
|
|
@@ -775,14 +806,16 @@ func typed_like(given: Variant, like: Variant, resolve: Callable) -> Dictionary:
|
|
|
775
806
|
##
|
|
776
807
|
## An object element given as a record goes to [param resolve] as a path would, which says how an
|
|
777
808
|
## object is named, rather than being refused as a map that cannot become the class.
|
|
778
|
-
func _element(
|
|
809
|
+
func _element(
|
|
810
|
+
item: Variant, builtin: int, declared: String, script: Script, resolve: Callable
|
|
811
|
+
) -> Dictionary:
|
|
779
812
|
if builtin == TYPE_NIL:
|
|
780
|
-
return {"value": item}
|
|
813
|
+
return { "value": item }
|
|
781
814
|
if builtin == TYPE_OBJECT and (item is String or item is Dictionary):
|
|
782
815
|
var named: Dictionary = resolve.call(item, declared, script)
|
|
783
816
|
if named.has("message"):
|
|
784
|
-
return {"message": str(named["message"]) + "."}
|
|
785
|
-
return {"value": named["object"]}
|
|
817
|
+
return { "message": str(named["message"]) + "." }
|
|
818
|
+
return { "value": named["object"] }
|
|
786
819
|
# Fitted alone, 4.5 into an Array[int] went in as 4, as a single value is refused for.
|
|
787
820
|
if cuts(item, builtin):
|
|
788
821
|
return {
|
|
@@ -790,18 +823,20 @@ func _element(item: Variant, builtin: int, declared: String, script: Script, res
|
|
|
790
823
|
(
|
|
791
824
|
"is %s, which %s cannot hold without changing it."
|
|
792
825
|
% [JSON.stringify(serialize(item), "", false), cut_by(builtin)]
|
|
793
|
-
)
|
|
826
|
+
),
|
|
794
827
|
}
|
|
795
828
|
var rounded: String = inexact(item, builtin)
|
|
796
829
|
if not rounded.is_empty():
|
|
797
|
-
return {"message": "is too large to be sure of: %s." % rounded}
|
|
830
|
+
return { "message": "is too large to be sure of: %s." % rounded }
|
|
798
831
|
var fitted_item: Variant = fitted(item, builtin)
|
|
799
832
|
if not acceptable(fitted_item, builtin):
|
|
800
|
-
# The value as it was sent rather than its engine type: JSON numbers arrive as floats, so a
|
|
801
|
-
# the caller wrote was named "float".
|
|
833
|
+
# The value as it was sent rather than its engine type: JSON numbers arrive as floats, so a
|
|
834
|
+
# 7 the caller wrote was named "float".
|
|
802
835
|
var wanted: String = type_string(builtin)
|
|
803
836
|
if builtin == TYPE_OBJECT:
|
|
804
837
|
wanted = declared if script == null else script_named(script)
|
|
805
838
|
var sent: String = JSON.stringify(serialize(item), "", false)
|
|
806
|
-
return {
|
|
807
|
-
|
|
839
|
+
return {
|
|
840
|
+
"message": "is %s, which cannot become %s.%s" % [sent, wanted, remedy(item, builtin)],
|
|
841
|
+
}
|
|
842
|
+
return { "value": fitted_item }
|