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
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## Where a control drawing a list draws one of its items, for a click by words: a tab, a list entry,
|
|
4
|
-
## a tree row or a menu bar title. Each is drawn by the control rather than held as a node, so a
|
|
5
|
-
## on the control's path presses its middle, which is whichever item happens to sit there.
|
|
3
|
+
## a tree row or a menu bar title. Each is drawn by the control rather than held as a node, so a
|
|
4
|
+
## click on the control's path presses its middle, which is whichever item happens to sit there.
|
|
6
5
|
|
|
7
6
|
const GdhSays = preload("runtime_says.gd")
|
|
8
7
|
const GdhValues = preload("runtime_values.gd")
|
|
@@ -27,12 +26,16 @@ static func item_rect(control: Control, item: Variant, wanted: String) -> Rect2:
|
|
|
27
26
|
var list: ItemList = control as ItemList
|
|
28
27
|
if list != null:
|
|
29
28
|
var entry: int = item
|
|
30
|
-
var scrolled: Vector2 = Vector2(
|
|
29
|
+
var scrolled: Vector2 = Vector2(
|
|
30
|
+
list.get_h_scroll_bar().value, list.get_v_scroll_bar().value
|
|
31
|
+
)
|
|
31
32
|
var placed: Rect2 = list.get_item_rect(entry, false)
|
|
32
33
|
placed.position -= scrolled
|
|
33
34
|
var reached: bool = bounds.has_point(placed.get_center())
|
|
34
35
|
return (
|
|
35
|
-
placed
|
|
36
|
+
placed
|
|
37
|
+
if reached and list.get_item_at_position(placed.get_center(), true) == entry
|
|
38
|
+
else Rect2()
|
|
36
39
|
)
|
|
37
40
|
var tree: Tree = control as Tree
|
|
38
41
|
if tree != null:
|
|
@@ -63,9 +66,9 @@ static func tree_column(tree: Tree, row: TreeItem, wanted: String) -> int:
|
|
|
63
66
|
## numbers its items, and what it says.
|
|
64
67
|
static func item_named(control: Control, item: Variant, says: String) -> Dictionary:
|
|
65
68
|
if control is Tree:
|
|
66
|
-
return {"kind": "row", "says": says}
|
|
69
|
+
return { "kind": "row", "says": says }
|
|
67
70
|
var kind: String = "tab" if control is TabBar else ("menu" if control is MenuBar else "item")
|
|
68
|
-
return {"kind": kind, "index": item, "says": says}
|
|
71
|
+
return { "kind": kind, "index": item, "says": says }
|
|
69
72
|
|
|
70
73
|
|
|
71
74
|
## Where in [param control] to press for [param item], the one of its items a click by
|
|
@@ -75,10 +78,17 @@ static func item_named(control: Control, item: Variant, says: String) -> Diction
|
|
|
75
78
|
## a tab or an entry into view before pressing it. A disabled item is refused as a disabled button
|
|
76
79
|
## is. A tree row is pressed on the cell saying the words, whose column goes into [param found].
|
|
77
80
|
static func in_view(
|
|
78
|
-
scene: SceneTree,
|
|
81
|
+
scene: SceneTree,
|
|
82
|
+
control: Control,
|
|
83
|
+
node_path: String,
|
|
84
|
+
item: Variant,
|
|
85
|
+
wanted: String,
|
|
86
|
+
found: Dictionary
|
|
79
87
|
) -> Dictionary:
|
|
80
88
|
var named: Dictionary = found.get("item", {})
|
|
81
|
-
var naming: String =
|
|
89
|
+
var naming: String = (
|
|
90
|
+
'the %s "%s" of %s' % [named.get("kind", "item"), named.get("says", ""), node_path]
|
|
91
|
+
)
|
|
82
92
|
var index: int = item if item is int else -1
|
|
83
93
|
var bar: TabBar = control as TabBar
|
|
84
94
|
var list: ItemList = control as ItemList
|
|
@@ -88,7 +98,9 @@ static func in_view(
|
|
|
88
98
|
or (list != null and list.is_item_disabled(index))
|
|
89
99
|
or (menus != null and menus.is_menu_disabled(index))
|
|
90
100
|
):
|
|
91
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
"type": "error", "message": "%s is disabled, so clicking it presses nothing" % naming,
|
|
103
|
+
}
|
|
92
104
|
var placed: Rect2 = item_rect(control, item, wanted)
|
|
93
105
|
var scrolled: bool = false
|
|
94
106
|
if placed.size == Vector2.ZERO:
|
|
@@ -101,14 +113,18 @@ static func in_view(
|
|
|
101
113
|
return {
|
|
102
114
|
"type": "error",
|
|
103
115
|
"message":
|
|
104
|
-
|
|
116
|
+
(
|
|
117
|
+
"%s went while it was being scrolled into view, so nothing was clicked"
|
|
118
|
+
% naming
|
|
119
|
+
),
|
|
105
120
|
}
|
|
106
121
|
placed = item_rect(control, item, wanted)
|
|
107
122
|
if placed.size == Vector2.ZERO:
|
|
108
123
|
var tried: String = " even scrolled to" if scrolled else ""
|
|
109
124
|
return {
|
|
110
125
|
"type": "error",
|
|
111
|
-
"message":
|
|
126
|
+
"message":
|
|
127
|
+
"%s is drawn nowhere a pointer reaches%s, so nothing was clicked" % [naming, tried],
|
|
112
128
|
}
|
|
113
129
|
var tree: Tree = control as Tree
|
|
114
130
|
if tree != null:
|
|
@@ -116,7 +132,7 @@ static func in_view(
|
|
|
116
132
|
var column: int = tree_column(tree, row, wanted)
|
|
117
133
|
if column != -1:
|
|
118
134
|
named["column"] = column
|
|
119
|
-
return {"point": placed.get_center(), "scrolled": scrolled}
|
|
135
|
+
return { "point": placed.get_center(), "scrolled": scrolled }
|
|
120
136
|
|
|
121
137
|
|
|
122
138
|
## Where [param menus] draws the title of [param menu], worked out as the engine works it out,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## What a pointer call names: a point, the stroke of a drag, a mouse button and its mask bit. Read
|
|
4
3
|
## here and sent from runtime_input.gd, which keeps where the pointer is.
|
|
5
4
|
|
|
@@ -29,14 +28,17 @@ static func point_in(params: Dictionary, node: Node, node_path: String, has_item
|
|
|
29
28
|
return null
|
|
30
29
|
if node is Node3D:
|
|
31
30
|
return (
|
|
32
|
-
|
|
31
|
+
(
|
|
32
|
+
"%s is a 3D node, clicked where it is drawn; x and y name a point in a Control's "
|
|
33
|
+
+ "own space"
|
|
34
|
+
)
|
|
33
35
|
% node_path
|
|
34
36
|
)
|
|
35
37
|
if has_item:
|
|
36
38
|
return (
|
|
37
39
|
(
|
|
38
|
-
"the words name an item %s draws, which is pressed where it is drawn, and x and y
|
|
39
|
-
+ " give one of them"
|
|
40
|
+
"the words name an item %s draws, which is pressed where it is drawn, and x and y "
|
|
41
|
+
+ "a point in it: give one of them"
|
|
40
42
|
)
|
|
41
43
|
% node_path
|
|
42
44
|
)
|
|
@@ -58,17 +60,17 @@ static func aim_of(params: Dictionary, root: Window) -> Variant:
|
|
|
58
60
|
return standing["message"]
|
|
59
61
|
var node: Node = standing["node"]
|
|
60
62
|
if node is CanvasItem or node is Viewport:
|
|
61
|
-
return {"node": node, "path": path}
|
|
63
|
+
return { "node": node, "path": path }
|
|
62
64
|
return (
|
|
63
65
|
"%s is a %s, and the points are in a Control's, a Node2D's or a Window's own space"
|
|
64
66
|
% [path, node.get_class()]
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
|
|
68
|
-
## Where [param local], in the space of the node [param aim] names, arrives: {"viewport",
|
|
69
|
-
## the viewport the pointer reaches it through and the point in that viewport's pixels,
|
|
70
|
-
## an event sent there names. A Window's space is its own pixels, as the game's window
|
|
71
|
-
## root's; a canvas item's is its own, the one it draws and reads _gui_input in.
|
|
70
|
+
## Where [param local], in the space of the node [param aim] names, arrives: {"viewport",
|
|
71
|
+
## "position"}, the viewport the pointer reaches it through and the point in that viewport's pixels,
|
|
72
|
+
## which is what an event sent there names. A Window's space is its own pixels, as the game's window
|
|
73
|
+
## pixels are the root's; a canvas item's is its own, the one it draws and reads _gui_input in.
|
|
72
74
|
static func carried(aim: Dictionary, local: Vector2, root: Window) -> Dictionary:
|
|
73
75
|
var node: Node = aim.get("node", root)
|
|
74
76
|
var reached: Dictionary = {}
|
|
@@ -77,10 +79,12 @@ static func carried(aim: Dictionary, local: Vector2, root: Window) -> Dictionary
|
|
|
77
79
|
reached = GdhScreen.reach(viewport, viewport.get_final_transform().affine_inverse() * local)
|
|
78
80
|
else:
|
|
79
81
|
var item: CanvasItem = node
|
|
80
|
-
reached = GdhScreen.reach(
|
|
82
|
+
reached = GdhScreen.reach(
|
|
83
|
+
item.get_viewport(), item.get_global_transform_with_canvas() * local
|
|
84
|
+
)
|
|
81
85
|
var arriving: Viewport = reached["viewport"]
|
|
82
86
|
var point: Vector2 = reached["point"]
|
|
83
|
-
return {"viewport": arriving, "position": arriving.get_final_transform() * point}
|
|
87
|
+
return { "viewport": arriving, "position": arriving.get_final_transform() * point }
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
## What a pointer answer says of where [param points] went in [param viewport]: the node they were
|
|
@@ -96,12 +100,15 @@ static func where(aim: Dictionary, viewport: Viewport, points: Array[Vector2]) -
|
|
|
96
100
|
|
|
97
101
|
|
|
98
102
|
## The points of a drag: x, y, then each of via, then toX, toY. Answers the String saying what is
|
|
99
|
-
## wrong instead, since a point left out would otherwise read as the window's corner.
|
|
100
|
-
|
|
103
|
+
## wrong instead, since a point left out would otherwise read as the window's corner. [param op] is
|
|
104
|
+
## the drag the refusal names.
|
|
105
|
+
static func stroke_of(params: Dictionary, op: String = "mouse_drag") -> Variant:
|
|
101
106
|
for key: String in ["x", "y", "toX", "toY"]:
|
|
102
107
|
if not params.has(key):
|
|
103
|
-
return "
|
|
104
|
-
var points: Array[Vector2] = [
|
|
108
|
+
return "%s needs x, y, toX and toY: %s is missing." % [op, key]
|
|
109
|
+
var points: Array[Vector2] = [
|
|
110
|
+
Vector2(GdhRead.as_float(params["x"]), GdhRead.as_float(params["y"])),
|
|
111
|
+
]
|
|
105
112
|
var via: Variant = params.get("via", [])
|
|
106
113
|
if not via is Array:
|
|
107
114
|
return "via must be a list of [x, y] points."
|
|
@@ -171,7 +178,9 @@ static func button_event(
|
|
|
171
178
|
##
|
|
172
179
|
## The points are window pixels and the visible rect is canvas units, which differ by the window's
|
|
173
180
|
## content scale and any letterbox offset, so the rect is taken into window pixels first.
|
|
174
|
-
static func outside_noted(
|
|
181
|
+
static func outside_noted(
|
|
182
|
+
answer: Dictionary, viewport: Viewport, points: Array[Vector2]
|
|
183
|
+
) -> Dictionary:
|
|
175
184
|
var shown: Rect2 = viewport.get_final_transform() * viewport.get_visible_rect()
|
|
176
185
|
var which: String = (
|
|
177
186
|
"the window's" if viewport == viewport.get_tree().root else "%s's" % viewport.get_path()
|
|
@@ -195,9 +204,12 @@ static func no_such_button(named: Variant) -> Dictionary:
|
|
|
195
204
|
"type": "error",
|
|
196
205
|
"message":
|
|
197
206
|
(
|
|
198
|
-
|
|
207
|
+
(
|
|
208
|
+
"%s is not a mouse button. It takes: left, right, middle, wheel_up, wheel_down, or "
|
|
209
|
+
+ "the number of one."
|
|
210
|
+
)
|
|
199
211
|
% JSON.stringify(named)
|
|
200
|
-
)
|
|
212
|
+
),
|
|
201
213
|
}
|
|
202
214
|
|
|
203
215
|
|
|
@@ -238,7 +250,10 @@ static func button_of(raw: Variant) -> int:
|
|
|
238
250
|
## whether the game is.
|
|
239
251
|
static func takes_no_click(control: Control, node_path: String, paused: bool) -> String:
|
|
240
252
|
if not control.is_visible_in_tree():
|
|
241
|
-
return
|
|
253
|
+
return (
|
|
254
|
+
"%s is not visible%s, so nothing can click it"
|
|
255
|
+
% [node_path, GdhTargets.why_hidden(control)]
|
|
256
|
+
)
|
|
242
257
|
var button: BaseButton = control as BaseButton
|
|
243
258
|
if button != null and button.disabled:
|
|
244
259
|
return "%s is disabled, so clicking it presses nothing" % node_path
|
|
@@ -258,7 +273,10 @@ static func takes_no_click(control: Control, node_path: String, paused: bool) ->
|
|
|
258
273
|
var cover: String = "" if over == null else "; %s is drawn over it" % over.get_path()
|
|
259
274
|
if paused:
|
|
260
275
|
return (
|
|
261
|
-
|
|
276
|
+
(
|
|
277
|
+
"the game is paused and %s does not process while it is (%s), so clicking it "
|
|
278
|
+
+ "does nothing%s"
|
|
279
|
+
)
|
|
262
280
|
% [node_path, why, cover]
|
|
263
281
|
)
|
|
264
282
|
return "%s does not process (%s), so clicking it does nothing%s" % [node_path, why, cover]
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## What reaches the running game the way a controller reaches it: a button pressed and a stick or
|
|
4
3
|
## trigger moved, as the events a pad sends.
|
|
5
4
|
##
|
|
@@ -10,8 +9,8 @@ extends RefCounted
|
|
|
10
9
|
|
|
11
10
|
const GdhRead = preload("reading.gd")
|
|
12
11
|
const GdhValues = preload("runtime_values.gd")
|
|
13
|
-
|
|
14
|
-
##
|
|
12
|
+
## Controller buttons by the names Godot gives them, for a caller naming one rather than numbering
|
|
13
|
+
## it.
|
|
15
14
|
const JOY_BUTTONS: Dictionary[String, int] = {
|
|
16
15
|
"a": JOY_BUTTON_A,
|
|
17
16
|
"b": JOY_BUTTON_B,
|
|
@@ -35,7 +34,6 @@ const JOY_BUTTONS: Dictionary[String, int] = {
|
|
|
35
34
|
"paddle4": JOY_BUTTON_PADDLE4,
|
|
36
35
|
"touchpad": JOY_BUTTON_TOUCHPAD,
|
|
37
36
|
}
|
|
38
|
-
|
|
39
37
|
## Stick and trigger axes by the names Godot gives them.
|
|
40
38
|
const JOY_AXES: Dictionary[String, int] = {
|
|
41
39
|
"left_x": JOY_AXIS_LEFT_X,
|
|
@@ -53,19 +51,21 @@ func _init(host: Node) -> void:
|
|
|
53
51
|
_host = host
|
|
54
52
|
|
|
55
53
|
|
|
56
|
-
## Presses a controller button, and lets go of it again unless asked to hold it, as a key press
|
|
57
|
-
## a button left down is a press that never ends, and everything reading it goes on seeing it.
|
|
54
|
+
## Presses a controller button, and lets go of it again unless asked to hold it, as a key press
|
|
55
|
+
## does: a button left down is a press that never ends, and everything reading it goes on seeing it.
|
|
58
56
|
func inject_joypad_button(params: Dictionary) -> Dictionary:
|
|
59
57
|
var given: Variant = params.get("button", null)
|
|
60
58
|
var button: int = _named_or_numbered(given, JOY_BUTTONS, JOY_BUTTON_MAX)
|
|
61
59
|
if button < 0:
|
|
62
|
-
return {
|
|
60
|
+
return {
|
|
61
|
+
"type": "error", "message": _not_one_of("button", given, JOY_BUTTONS, JOY_BUTTON_MAX),
|
|
62
|
+
}
|
|
63
63
|
var device: int = GdhRead.as_int(params.get("device", 0), 0)
|
|
64
64
|
var held: bool = GdhRead.as_bool(params.get("pressed", true), true)
|
|
65
65
|
var whole: bool = not params.has("pressed")
|
|
66
66
|
var holding: Variant = GdhValues.held_for(params)
|
|
67
67
|
if holding is String:
|
|
68
|
-
return {"type": "error", "message": holding}
|
|
68
|
+
return { "type": "error", "message": holding }
|
|
69
69
|
var frames: int = holding
|
|
70
70
|
|
|
71
71
|
var focused: String = GdhValues.focus_path(_host)
|
|
@@ -73,8 +73,8 @@ func inject_joypad_button(params: Dictionary) -> Dictionary:
|
|
|
73
73
|
Input.parse_input_event(event)
|
|
74
74
|
var lasted: Variant = null
|
|
75
75
|
if whole:
|
|
76
|
-
# A frame between the halves at least, as a key has, so a game that acts on the press and
|
|
77
|
-
# that acts on the release each get a frame to do it in.
|
|
76
|
+
# A frame between the halves at least, as a key has, so a game that acts on the press and
|
|
77
|
+
# one that acts on the release each get a frame to do it in.
|
|
78
78
|
lasted = await GdhValues.hold(_host.get_tree(), frames)
|
|
79
79
|
Input.parse_input_event(_button(button, device, false))
|
|
80
80
|
# The engine hands a parsed event on at the next frame, so the focus is read after one.
|
|
@@ -101,13 +101,17 @@ func inject_joypad_motion(params: Dictionary) -> Dictionary:
|
|
|
101
101
|
var given: Variant = params.get("axis", null)
|
|
102
102
|
var axis: int = _named_or_numbered(given, JOY_AXES, JOY_AXIS_MAX)
|
|
103
103
|
if axis < 0:
|
|
104
|
-
return {"type": "error", "message": _not_one_of("axis", given, JOY_AXES, JOY_AXIS_MAX)}
|
|
104
|
+
return { "type": "error", "message": _not_one_of("axis", given, JOY_AXES, JOY_AXIS_MAX) }
|
|
105
105
|
if not params.has("value"):
|
|
106
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
"type": "error",
|
|
108
|
+
"message": "joypad_motion needs value, where the axis is, -1 to 1",
|
|
109
|
+
}
|
|
107
110
|
var value: float = GdhRead.as_float(params.get("value"), 0.0)
|
|
108
111
|
if value < -1.0 or value > 1.0:
|
|
109
112
|
return {
|
|
110
|
-
"type": "error",
|
|
113
|
+
"type": "error",
|
|
114
|
+
"message": "value %s is outside -1 to 1, the whole travel of an axis" % value,
|
|
111
115
|
}
|
|
112
116
|
var device: int = GdhRead.as_int(params.get("device", 0), 0)
|
|
113
117
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## Colon paths through what a node holds: "/root/Main" with "_game:run:day" reaches the day off the
|
|
4
3
|
## run off the game the node keeps. Every op that reads, writes, calls or waits on a game's state
|
|
5
4
|
## walks the same way, so the walk and what it says when a step is not there live in one place.
|
|
@@ -7,7 +6,6 @@ extends RefCounted
|
|
|
7
6
|
const GdhRead = preload("reading.gd")
|
|
8
7
|
const GdhSerialisation = preload("serialisation.gd")
|
|
9
8
|
const GdhValues = preload("runtime_values.gd")
|
|
10
|
-
|
|
11
9
|
## The calls a path makes on a list or a map: those that take no arguments and change nothing.
|
|
12
10
|
##
|
|
13
11
|
## Named rather than asked of the engine, because asking about a method a list does not have logs an
|
|
@@ -50,8 +48,8 @@ const MAP_CALLS: Array[String] = [
|
|
|
50
48
|
"get_typed_value_class_name",
|
|
51
49
|
"get_typed_value_script",
|
|
52
50
|
]
|
|
53
|
-
## The calls a path makes on a packed list, which has fewer than a list: measured on 4.7.2, it has
|
|
54
|
-
## front(), back(), max(), min() or hash().
|
|
51
|
+
## The calls a path makes on a packed list, which has fewer than a list: measured on 4.7.2, it has
|
|
52
|
+
## no front(), back(), max(), min() or hash().
|
|
55
53
|
const PACKED_CALLS: Array[String] = ["size", "is_empty", "duplicate"]
|
|
56
54
|
## What a packed list of bytes answers beside those, read as another list or as text. It has no
|
|
57
55
|
## to_byte_array(), which every other packed list has.
|
|
@@ -109,7 +107,7 @@ const LIST_READS: Array[String] = [
|
|
|
109
107
|
"is_same_typed",
|
|
110
108
|
]
|
|
111
109
|
const MAP_CHANGES: Array[String] = [
|
|
112
|
-
"clear", "assign", "sort", "merge", "erase", "get_or_add", "set", "make_read_only"
|
|
110
|
+
"clear", "assign", "sort", "merge", "erase", "get_or_add", "set", "make_read_only",
|
|
113
111
|
]
|
|
114
112
|
const MAP_READS: Array[String] = [
|
|
115
113
|
"merged",
|
|
@@ -191,7 +189,8 @@ const VALUE_CALLS: Dictionary = {
|
|
|
191
189
|
"max_axis_index",
|
|
192
190
|
"min_axis_index",
|
|
193
191
|
],
|
|
194
|
-
TYPE_VECTOR2I:
|
|
192
|
+
TYPE_VECTOR2I:
|
|
193
|
+
["length", "length_squared", "abs", "sign", "aspect", "max_axis_index", "min_axis_index"],
|
|
195
194
|
TYPE_VECTOR3:
|
|
196
195
|
[
|
|
197
196
|
"length",
|
|
@@ -266,14 +265,16 @@ const VALUE_CALLS: Dictionary = {
|
|
|
266
265
|
"get_axis",
|
|
267
266
|
],
|
|
268
267
|
TYPE_PLANE: ["normalized", "is_finite", "get_center"],
|
|
269
|
-
TYPE_AABB:
|
|
268
|
+
TYPE_AABB:
|
|
269
|
+
["abs", "is_finite", "get_center", "get_volume", "get_longest_axis", "get_shortest_axis"],
|
|
270
270
|
TYPE_PROJECTION: ["inverse", "determinant"],
|
|
271
271
|
}
|
|
272
272
|
## The calls an empty list has nothing to answer, which the engine reports as an error when asked.
|
|
273
273
|
const NEEDS_AN_ELEMENT: Array[String] = ["front", "back", "pick_random"]
|
|
274
|
-
|
|
275
274
|
## Said after every call a path refuses for doing something rather than reading.
|
|
276
|
-
const READS_ONLY: String =
|
|
275
|
+
const READS_ONLY: String = (
|
|
276
|
+
", and a path, which a wait reads every frame, only calls methods that read"
|
|
277
|
+
)
|
|
277
278
|
|
|
278
279
|
|
|
279
280
|
## What [param reaching] names something on, which is the node itself until the name has a colon
|
|
@@ -315,8 +316,8 @@ static func walk_to(node: Object, node_path: String, reaching: String) -> Dictio
|
|
|
315
316
|
for step: int in parts.size() - 1:
|
|
316
317
|
var named: String = parts[step]
|
|
317
318
|
if not can_read(holder, named):
|
|
318
|
-
return {"type": "error", "message": nothing_there(holder, named, called)}
|
|
319
|
-
chain.append({"holder": holder, "named": named, "called": called})
|
|
319
|
+
return { "type": "error", "message": nothing_there(holder, named, called) }
|
|
320
|
+
chain.append({ "holder": holder, "named": named, "called": called })
|
|
320
321
|
holder = read_under(holder, named)
|
|
321
322
|
called = "%s:%s" % [called, named]
|
|
322
323
|
if not can_hold(holder):
|
|
@@ -327,9 +328,9 @@ static func walk_to(node: Object, node_path: String, reaching: String) -> Dictio
|
|
|
327
328
|
(
|
|
328
329
|
"%s holds %s, which has nothing to read %s off"
|
|
329
330
|
% [called, type_string(typeof(holder)), wanted]
|
|
330
|
-
)
|
|
331
|
+
),
|
|
331
332
|
}
|
|
332
|
-
return {"holder": holder, "name": parts[parts.size() - 1], "called": called, "chain": chain}
|
|
333
|
+
return { "holder": holder, "name": parts[parts.size() - 1], "called": called, "chain": chain }
|
|
333
334
|
|
|
334
335
|
|
|
335
336
|
## The object [param given] names for a method's argument, as `{"object": ...}`, or as
|
|
@@ -347,7 +348,7 @@ static func object_at(root: Node, node: Node, node_path: String, given: Variant)
|
|
|
347
348
|
(
|
|
348
349
|
"an object is named by its path, not by the record runtime_inspect answered with:"
|
|
349
350
|
+ " its id does not survive the trip as a number"
|
|
350
|
-
)
|
|
351
|
+
),
|
|
351
352
|
}
|
|
352
353
|
var path: String = given
|
|
353
354
|
var start: Node = node
|
|
@@ -358,26 +359,27 @@ static func object_at(root: Node, node: Node, node_path: String, given: Variant)
|
|
|
358
359
|
start_path = path if colon == -1 else path.substr(0, colon)
|
|
359
360
|
var standing: Dictionary = GdhValues.node_at(root, start_path)
|
|
360
361
|
if standing.has("message"):
|
|
361
|
-
return {"message": str(standing["message"])}
|
|
362
|
+
return { "message": str(standing["message"]) }
|
|
362
363
|
start = standing["node"]
|
|
363
364
|
if colon == -1:
|
|
364
|
-
return {"object": start}
|
|
365
|
+
return { "object": start }
|
|
365
366
|
reaching = path.substr(colon + 1)
|
|
366
367
|
if reaching.is_empty():
|
|
367
|
-
return {"message": "%s names nothing past the node" % path}
|
|
368
|
+
return { "message": "%s names nothing past the node" % path }
|
|
368
369
|
var reached: Dictionary = walk_to(start, start_path, reaching)
|
|
369
370
|
if reached.has("message"):
|
|
370
|
-
return {"message": str(reached["message"])}
|
|
371
|
+
return { "message": str(reached["message"]) }
|
|
371
372
|
var holder: Variant = reached["holder"]
|
|
372
373
|
var name: String = reached["name"]
|
|
373
374
|
if not can_read(holder, name):
|
|
374
|
-
return {"message": nothing_there(holder, name, str(reached["called"]))}
|
|
375
|
+
return { "message": nothing_there(holder, name, str(reached["called"])) }
|
|
375
376
|
var value: Variant = read_under(holder, name)
|
|
376
377
|
if not value is Object or not is_instance_valid(value):
|
|
377
378
|
return {
|
|
378
|
-
"message":
|
|
379
|
+
"message":
|
|
380
|
+
"%s:%s holds %s, not an object" % [reached["called"], name, type_string(typeof(value))],
|
|
379
381
|
}
|
|
380
|
-
return {"object": value}
|
|
382
|
+
return { "object": value }
|
|
381
383
|
|
|
382
384
|
|
|
383
385
|
## Whether a step can be taken into [param value] at all: an object, a list, a map or a value with
|
|
@@ -444,8 +446,9 @@ static func value_calls(value: Variant) -> Array:
|
|
|
444
446
|
return VALUE_CALLS.get(typeof(value), [])
|
|
445
447
|
|
|
446
448
|
|
|
447
|
-
## Whether [param value] is a packed list. Stepped into the way a list is, by index, and a write
|
|
448
|
-
## one reaches the game's own copy, since packed lists share their storage until one is
|
|
449
|
+
## Whether [param value] is a packed list. Stepped into the way a list is, by index, and a write
|
|
450
|
+
## into one reaches the game's own copy, since packed lists share their storage until one is
|
|
451
|
+
## duplicated.
|
|
449
452
|
static func packed(value: Variant) -> bool:
|
|
450
453
|
return typeof(value) in PACKED
|
|
451
454
|
|
|
@@ -468,7 +471,7 @@ static func nothing_under(holder: Variant, named: String, called: String) -> Str
|
|
|
468
471
|
## way GDScript's own does, so reading the last of something does not mean asking how many first.
|
|
469
472
|
static func can_read(holder: Variant, named: String) -> bool:
|
|
470
473
|
var method: String = method_of(named)
|
|
471
|
-
var given: Dictionary = arguments_of(named) if not method.is_empty() else {"args": []}
|
|
474
|
+
var given: Dictionary = arguments_of(named) if not method.is_empty() else { "args": [] }
|
|
472
475
|
if given.has("message"):
|
|
473
476
|
return false
|
|
474
477
|
var args: Array = given["args"]
|
|
@@ -507,8 +510,8 @@ static func can_read(holder: Variant, named: String) -> bool:
|
|
|
507
510
|
## The key of [param map] a step names, in a list of one, or an empty list when it names none.
|
|
508
511
|
##
|
|
509
512
|
## A step is text, and a map is keyed by whatever the game put in it: text, a StringName, or a
|
|
510
|
-
## number, which a map of units by id is. Keyed by 3, "3" is not a key of it, measured on 4.7.2, so
|
|
511
|
-
## step that reads as a number is tried as one after the two spellings of text.
|
|
513
|
+
## number, which a map of units by id is. Keyed by 3, "3" is not a key of it, measured on 4.7.2, so
|
|
514
|
+
## a step that reads as a number is tried as one after the two spellings of text.
|
|
512
515
|
##
|
|
513
516
|
## A map typed by its key is asked only in that type. It checks every key it is asked about and
|
|
514
517
|
## reports an engine error for one of another type, so a step into a map typed by an enum read the
|
|
@@ -611,13 +614,15 @@ static func method_of(named: String) -> String:
|
|
|
611
614
|
## whole number as an int. A path names what is read, and an argument that was itself a path or an
|
|
612
615
|
## object would make a read depend on other state in a way the step does not show.
|
|
613
616
|
static func arguments_of(named: String) -> Dictionary:
|
|
614
|
-
var inside: String =
|
|
617
|
+
var inside: String = (
|
|
618
|
+
named.substr(named.find("(") + 1, named.length() - named.find("(") - 2).strip_edges()
|
|
619
|
+
)
|
|
615
620
|
if inside.is_empty():
|
|
616
|
-
return {"args": []}
|
|
621
|
+
return { "args": [] }
|
|
617
622
|
var reader: JSON = JSON.new()
|
|
618
623
|
var parsed: Variant = reader.data if reader.parse("[%s]" % inside) == OK else null
|
|
619
624
|
if not parsed is Array:
|
|
620
|
-
return {"message": _not_literals(named)}
|
|
625
|
+
return { "message": _not_literals(named) }
|
|
621
626
|
var args: Array = []
|
|
622
627
|
for given: Variant in parsed:
|
|
623
628
|
if given is float:
|
|
@@ -629,13 +634,16 @@ static func arguments_of(named: String) -> Dictionary:
|
|
|
629
634
|
elif given == null or given is String or given is bool:
|
|
630
635
|
args.append(given)
|
|
631
636
|
else:
|
|
632
|
-
return {"message": _not_literals(named)}
|
|
633
|
-
return {"args": args}
|
|
637
|
+
return { "message": _not_literals(named) }
|
|
638
|
+
return { "args": args }
|
|
634
639
|
|
|
635
640
|
|
|
636
641
|
static func _not_literals(named: String) -> String:
|
|
637
642
|
return (
|
|
638
|
-
|
|
643
|
+
(
|
|
644
|
+
"%s: a path gives a call literal arguments only, words in double quotes, numbers, "
|
|
645
|
+
+ "true, false or null"
|
|
646
|
+
)
|
|
639
647
|
% named
|
|
640
648
|
)
|
|
641
649
|
|
|
@@ -663,8 +671,8 @@ static func _arity(object: Object, method: String) -> Dictionary:
|
|
|
663
671
|
## when it may.
|
|
664
672
|
##
|
|
665
673
|
## A path is read, and a wait reads it every frame, so a call in one has to be a read. The engine
|
|
666
|
-
## marks its own reading methods const and `queue_free()`, `free()` and `set_name()` not, measured
|
|
667
|
-
## 4.7.2, though it leaves some reads unmarked as well, `Engine.get_frames_drawn()` and
|
|
674
|
+
## marks its own reading methods const and `queue_free()`, `free()` and `set_name()` not, measured
|
|
675
|
+
## on 4.7.2, though it leaves some reads unmarked as well, `Engine.get_frames_drawn()` and
|
|
668
676
|
## `get_time_scale()` among them; so the refusal says the mark is missing, not that the method
|
|
669
677
|
## changes anything, and points at runtime_invoke call, which calls it. A game's methods carry no
|
|
670
678
|
## such mark, so one declared `-> void` is taken at its word, as doing something and answering
|
|
@@ -675,7 +683,9 @@ static func _arity(object: Object, method: String) -> Dictionary:
|
|
|
675
683
|
## Given [param given] arguments, a call is the engine's and marked as reading, or it is refused. A
|
|
676
684
|
## game's method taking arguments is trusted on nothing at all: one answering a value can as well
|
|
677
685
|
## change what it was asked about, and runtime_invoke call is how one is called on purpose.
|
|
678
|
-
static func call_refused(
|
|
686
|
+
static func call_refused(
|
|
687
|
+
object: Object, method: String, called_as: String, given: int = 0
|
|
688
|
+
) -> String:
|
|
679
689
|
var arity: Dictionary = _arity(object, method)
|
|
680
690
|
if arity.is_empty():
|
|
681
691
|
return with_methods_offered("%s has no method %s" % [called_as, method], object, method)
|
|
@@ -729,7 +739,8 @@ static func with_methods_offered(said: String, object: Object, method: String) -
|
|
|
729
739
|
var script: Script = attached
|
|
730
740
|
for entry: Dictionary in script.get_script_method_list():
|
|
731
741
|
var name: String = str(entry.get("name", ""))
|
|
732
|
-
# A class called by its name runs only its static functions, so offering the rest leads
|
|
742
|
+
# A class called by its name runs only its static functions, so offering the rest leads
|
|
743
|
+
# to a refusal.
|
|
733
744
|
var flags: int = entry.get("flags", 0)
|
|
734
745
|
var callable: bool = not object is Script or flags & METHOD_FLAG_STATIC
|
|
735
746
|
if callable and not name.is_empty() and not own.has(name):
|
|
@@ -741,7 +752,10 @@ static func with_methods_offered(said: String, object: Object, method: String) -
|
|
|
741
752
|
every.append(name)
|
|
742
753
|
var close: PackedStringArray = GdhRead.nearest(method, PackedStringArray(every))
|
|
743
754
|
if not close.is_empty():
|
|
744
|
-
return
|
|
755
|
+
return (
|
|
756
|
+
"%s; the nearest it has %s %s"
|
|
757
|
+
% [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
758
|
+
)
|
|
745
759
|
if own.is_empty():
|
|
746
760
|
return said
|
|
747
761
|
own.sort()
|
|
@@ -808,7 +822,10 @@ static func unwritable_copy(chain: Array[Dictionary]) -> String:
|
|
|
808
822
|
var named: String = link["named"]
|
|
809
823
|
if not method_of(named).is_empty():
|
|
810
824
|
return (
|
|
811
|
-
|
|
825
|
+
(
|
|
826
|
+
"%s:%s is what a call answered, a copy the game keeps nowhere, so a part of it "
|
|
827
|
+
+ "is not a place to write"
|
|
828
|
+
)
|
|
812
829
|
% [link["called"], named]
|
|
813
830
|
)
|
|
814
831
|
if not composite(link["holder"]):
|
|
@@ -833,7 +850,13 @@ static func write_back(chain: Array[Dictionary], changed: Variant) -> void:
|
|
|
833
850
|
## Only ever called once [method can_read] agrees.
|
|
834
851
|
static func read_under(holder: Variant, named: String) -> Variant:
|
|
835
852
|
if (
|
|
836
|
-
(
|
|
853
|
+
(
|
|
854
|
+
holder is Array
|
|
855
|
+
or holder is Dictionary
|
|
856
|
+
or packed(holder)
|
|
857
|
+
or composite(holder)
|
|
858
|
+
or textual(holder)
|
|
859
|
+
)
|
|
837
860
|
and not method_of(named).is_empty()
|
|
838
861
|
):
|
|
839
862
|
return Callable.create(holder, method_of(named)).call()
|
|
@@ -876,7 +899,7 @@ static func _index_in(named: String, size: int) -> int:
|
|
|
876
899
|
## the whole path, because the step is the typo.
|
|
877
900
|
static func nothing_there(holder: Variant, named: String, called_as: String) -> String:
|
|
878
901
|
var container_call: String = method_of(named)
|
|
879
|
-
var given: Dictionary = arguments_of(named) if not container_call.is_empty() else {"args": []}
|
|
902
|
+
var given: Dictionary = arguments_of(named) if not container_call.is_empty() else { "args": [] }
|
|
880
903
|
if given.has("message"):
|
|
881
904
|
return "%s:%s" % [called_as, given["message"]]
|
|
882
905
|
var given_args: Array = given["args"]
|
|
@@ -893,14 +916,21 @@ static func nothing_there(holder: Variant, named: String, called_as: String) ->
|
|
|
893
916
|
return container_calls_refused(
|
|
894
917
|
called_as, "a packed list", container_call, given_count, packed_calls(holder)
|
|
895
918
|
)
|
|
896
|
-
return
|
|
919
|
+
return (
|
|
920
|
+
"%s is a packed list of %d, so there is no %s in it" % [called_as, len(holder), named]
|
|
921
|
+
)
|
|
897
922
|
if textual(holder):
|
|
898
923
|
if container_call.is_empty():
|
|
899
924
|
return (
|
|
900
|
-
|
|
925
|
+
(
|
|
926
|
+
"%s is a string, so a path makes only calls on it, which read it and take no "
|
|
927
|
+
+ "arguments: %s"
|
|
928
|
+
)
|
|
901
929
|
% [called_as, "(), ".join(STRING_CALLS) + "()"]
|
|
902
930
|
)
|
|
903
|
-
return container_calls_refused(
|
|
931
|
+
return container_calls_refused(
|
|
932
|
+
called_as, "a string", container_call, given_count, STRING_CALLS
|
|
933
|
+
)
|
|
904
934
|
if holder is Dictionary:
|
|
905
935
|
if not container_call.is_empty():
|
|
906
936
|
return container_calls_refused(called_as, "a map", container_call, given_count)
|
|
@@ -948,7 +978,10 @@ static func nothing_there(holder: Variant, named: String, called_as: String) ->
|
|
|
948
978
|
)
|
|
949
979
|
return "%s is %s, so it has no %s; it has %s" % [called_as, sort, named, has]
|
|
950
980
|
if not holder is Object:
|
|
951
|
-
return
|
|
981
|
+
return (
|
|
982
|
+
"%s is %s, which has nothing to read %s off"
|
|
983
|
+
% [called_as, type_string(typeof(holder)), named]
|
|
984
|
+
)
|
|
952
985
|
var object: Object = holder
|
|
953
986
|
var method: String = method_of(named)
|
|
954
987
|
if not method.is_empty():
|
|
@@ -975,12 +1008,17 @@ static func container_calls_refused(
|
|
|
975
1008
|
elif sort == "a list":
|
|
976
1009
|
calls = LIST_CALLS
|
|
977
1010
|
if given > 0 and method in calls:
|
|
978
|
-
return
|
|
1011
|
+
return (
|
|
1012
|
+
"%s is %s, and a path calls %s() on one with no arguments" % [called_as, sort, method]
|
|
1013
|
+
)
|
|
979
1014
|
var spelled: Array[String] = []
|
|
980
1015
|
for each: String in calls:
|
|
981
1016
|
spelled.append(each + "()")
|
|
982
1017
|
return (
|
|
983
|
-
|
|
1018
|
+
(
|
|
1019
|
+
"%s is %s, and %s() is not one of the calls a path makes on one, which read it and "
|
|
1020
|
+
+ "take no arguments: %s"
|
|
1021
|
+
)
|
|
984
1022
|
% [called_as, sort, method, ", ".join(spelled)]
|
|
985
1023
|
)
|
|
986
1024
|
|