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.
Files changed (107) hide show
  1. package/build/cli.js +131 -47
  2. package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
  3. package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
  4. package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
  5. package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
  6. package/build/godot/addons/gdharness_editor/file_reads.gd +114 -39
  7. package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
  8. package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
  9. package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
  10. package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
  11. package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
  12. package/build/godot/addons/gdharness_editor/reading.gd +37 -12
  13. package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
  14. package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
  15. package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
  16. package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
  17. package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
  18. package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
  19. package/build/godot/addons/gdharness_editor/tools/animation_states.gd +41 -28
  20. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
  21. package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
  22. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
  23. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
  24. package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
  25. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
  26. package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
  27. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +181 -107
  28. package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
  29. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
  30. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
  31. package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
  32. package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +66 -0
  33. package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
  34. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
  35. package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
  36. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
  37. package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
  38. package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
  39. package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
  40. package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
  41. package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
  42. package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
  43. package/build/godot/addons/gdharness_runtime/reading.gd +37 -12
  44. package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
  45. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
  46. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
  47. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
  48. package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
  49. package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
  50. package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
  51. package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
  52. package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
  53. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +38 -20
  54. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
  55. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
  56. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
  57. package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
  58. package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
  59. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
  60. package/build/godot/addons/gdharness_runtime/runtime_touch.gd +211 -0
  61. package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
  62. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
  63. package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
  64. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
  65. package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
  66. package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
  67. package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
  68. package/build/godot/addons/gdharness_tests/bench.gd +4 -5
  69. package/build/godot/addons/gdharness_tests/event.gd +10 -5
  70. package/build/godot/addons/gdharness_tests/share.gd +38 -18
  71. package/build/godot/addons/gdharness_tests/suites.gd +3 -3
  72. package/build/godot/addons/gdharness_tests/wait.gd +3 -2
  73. package/build/godot/loader/loader.gd +9 -9
  74. package/build/godot/operations/animation_reads.gd +84 -35
  75. package/build/godot/operations/audio_buses.gd +48 -19
  76. package/build/godot/operations/class_cache.gd +16 -11
  77. package/build/godot/operations/classdb_queries.gd +89 -44
  78. package/build/godot/operations/declared_values.gd +68 -32
  79. package/build/godot/operations/dependencies.gd +139 -93
  80. package/build/godot/operations/editor_tools.gd +9 -10
  81. package/build/godot/operations/export_presets.gd +100 -54
  82. package/build/godot/operations/file_reads.gd +114 -39
  83. package/build/godot/operations/file_walk.gd +12 -5
  84. package/build/godot/operations/gdscript_analysis.gd +105 -66
  85. package/build/godot/operations/gdscript_authoring.gd +138 -39
  86. package/build/godot/operations/godot_operations.gd +17 -11
  87. package/build/godot/operations/import_pipeline.gd +110 -59
  88. package/build/godot/operations/input_actions.gd +63 -39
  89. package/build/godot/operations/patterns.gd +3 -1
  90. package/build/godot/operations/plugins.gd +36 -16
  91. package/build/godot/operations/project_config.gd +144 -27
  92. package/build/godot/operations/project_diagnostics.gd +24 -11
  93. package/build/godot/operations/project_file.gd +16 -16
  94. package/build/godot/operations/project_validation.gd +71 -44
  95. package/build/godot/operations/property_paths.gd +22 -12
  96. package/build/godot/operations/property_values.gd +143 -78
  97. package/build/godot/operations/reading.gd +37 -12
  98. package/build/godot/operations/rename_checks.gd +2 -2
  99. package/build/godot/operations/resource_files.gd +11 -9
  100. package/build/godot/operations/same_values.gd +4 -4
  101. package/build/godot/operations/scene_connections.gd +27 -13
  102. package/build/godot/operations/script_check.gd +52 -49
  103. package/build/godot/operations/serialisation.gd +75 -51
  104. package/build/godot/operations/setting_reads.gd +7 -6
  105. package/build/godot/operations/value_shapes.gd +5 -6
  106. package/build/godot/tests/listed.gd +321 -268
  107. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  extends RefCounted
2
-
3
2
  ## What the server asks about the running tree: its shape, one node, the nodes matching a
4
3
  ## question, where one is on screen, a property's value, the metrics. What it changes, a property
5
4
  ## written or a method called, is in runtime_changes.gd.
@@ -11,19 +10,15 @@ const GdhSays = preload("runtime_says.gd")
11
10
  const GdhScreen = preload("runtime_screen.gd")
12
11
  const GdhValues = preload("runtime_values.gd")
13
12
  const GdhWords = preload("runtime_words.gd")
14
-
15
13
  ## The most nodes one find answers with, unless asked for fewer: enough for any real query and
16
14
  ## far short of the tree dump a query exists to avoid.
17
15
  const FIND_LIMIT: int = 100
18
-
19
16
  ## What a find can be narrowed by, which is also what it refuses to answer without. Named once so
20
17
  ## the refusal lists the same set the walk reads.
21
18
  const FILTERS: PackedStringArray = ["class", "script", "name", "group", "says"]
22
-
23
19
  ## The most lines one read answers with, unless asked for fewer. A screen is a few dozen; a
24
20
  ## thousand is a tree somebody pointed this at by mistake.
25
21
  const READ_LIMIT: int = 500
26
-
27
22
  ## The most either can be asked for. A limit above it is refused rather than cut to it: the ceiling
28
23
  ## was the default for a read, so the remedy its own answer gave, raising the limit, changed nothing
29
24
  ## and said nothing, and a caller told to raise a number that stays put concludes the screen moved.
@@ -59,7 +54,7 @@ func get_tree(params: Dictionary) -> Dictionary:
59
54
  for each: Variant in listed:
60
55
  named.append(str(each))
61
56
  else:
62
- return {"type": "error", "message": "properties must be a list of property names"}
57
+ return { "type": "error", "message": "properties must be a list of property names" }
63
58
 
64
59
  var reached: Dictionary = GdhValues.node_at(_host.get_tree().root, root_path)
65
60
  if reached.has("message"):
@@ -100,19 +95,24 @@ func find_nodes(params: Dictionary) -> Dictionary:
100
95
  var limit: int = GdhRead.as_int(params.get("limit", FIND_LIMIT), FIND_LIMIT)
101
96
  if limit < 1 or limit > LIMIT_CEILING:
102
97
  return {
103
- "type": "error", "message": "limit is from 1 to %d, and %d was asked for" % [LIMIT_CEILING, limit]
98
+ "type": "error",
99
+ "message": "limit is from 1 to %d, and %d was asked for" % [LIMIT_CEILING, limit],
104
100
  }
105
101
 
106
102
  if not _anything_asked(wanted):
107
- return {"type": "error", "message": "find_nodes needs at least one of " + ", ".join(FILTERS)}
103
+ return {
104
+ "type": "error", "message": "find_nodes needs at least one of " + ", ".join(FILTERS),
105
+ }
108
106
  if not wanted["script"].is_empty() and not wanted["script"].begins_with("res://"):
109
107
  wanted["script"] = "res://" + wanted["script"]
110
108
 
111
109
  # Split once here rather than for each node the walk passes.
112
110
  var names: Array[String] = GdhSays.split_at_bars(wanted["name"])
113
111
  if not wanted["name"].is_empty() and names.is_empty():
114
- var bars_only: String = 'name "%s" is only bars, so it names no node; write the names between them'
115
- return {"type": "error", "message": bars_only % wanted["name"]}
112
+ var bars_only: String = (
113
+ 'name "%s" is only bars, so it names no node; write the names between them'
114
+ )
115
+ return { "type": "error", "message": bars_only % wanted["name"] }
116
116
 
117
117
  var reached: Dictionary = GdhValues.node_at(_host.get_tree().root, root_path)
118
118
  if reached.has("message"):
@@ -164,7 +164,11 @@ func find_nodes(params: Dictionary) -> Dictionary:
164
164
  nearly += 1
165
165
  else:
166
166
  nearly_hidden += 1
167
- elif not widened_says.is_empty() and _named(node, names) and _matches_apart_from_name(node, widened):
167
+ elif (
168
+ not widened_says.is_empty()
169
+ and _named(node, names)
170
+ and _matches_apart_from_name(node, widened)
171
+ ):
168
172
  if include_hidden or readable(node):
169
173
  nearly_said += 1
170
174
  else:
@@ -179,11 +183,13 @@ func find_nodes(params: Dictionary) -> Dictionary:
179
183
  for index: int in range(children.size() - 1, -1, -1):
180
184
  pending.push_front(children[index])
181
185
 
182
- var answer: Dictionary = {"type": "nodes", "count": found.size(), "truncated": truncated, "nodes": found}
186
+ var answer: Dictionary = {
187
+ "type": "nodes", "count": found.size(), "truncated": truncated, "nodes": found,
188
+ }
183
189
  var notes: Array[String] = []
184
- # Each still listed, since a caller may mean one of them, and marked, so one sign drawn as a label
185
- # and its outline copies is not read as four signs. The outermost match it copies, since a find
186
- # under one of the copies has left the label they all copy outside it.
190
+ # Each still listed, since a caller may mean one of them, and marked, so one sign drawn as a
191
+ # label and its outline copies is not read as four signs. The outermost match it copies, since a
192
+ # find under one of the copies has left the label they all copy outside it.
187
193
  var copies: int = 0
188
194
  for index: int in range(found_nodes.size()):
189
195
  var at: int = -1
@@ -201,8 +207,8 @@ func find_nodes(params: Dictionary) -> Dictionary:
201
207
  . append(
202
208
  (
203
209
  (
204
- "%d of the %d matches %s drawn under a match saying the same words, as an outline or a"
205
- + " glow is drawn, and %s it under copyOf: %d distinct"
210
+ "%d of the %d matches %s drawn under a match saying the same words, as an "
211
+ + "outline or a glow is drawn, and %s it under copyOf: %d distinct"
206
212
  )
207
213
  % [
208
214
  copies,
@@ -230,7 +236,10 @@ func find_nodes(params: Dictionary) -> Dictionary:
230
236
  notes
231
237
  . append(
232
238
  (
233
- 'name is matched as a glob against the whole name; %d %snode %s %s, which "%s"%s would find'
239
+ (
240
+ "name is matched as a glob against the whole name; %d %snode %s %s, which "
241
+ + '"%s"%s would find'
242
+ )
234
243
  % [
235
244
  named_near,
236
245
  "" if nearly > 0 else "hidden or out-of-view ",
@@ -248,7 +257,10 @@ func find_nodes(params: Dictionary) -> Dictionary:
248
257
  notes
249
258
  . append(
250
259
  (
251
- 'says with a wildcard is matched as a glob against a node\'s whole text; "%s"%s would find %d %snode%s'
260
+ (
261
+ "says with a wildcard is matched as a glob against a node\'s whole text; "
262
+ + '"%s"%s would find %d %snode%s'
263
+ )
252
264
  % [
253
265
  widened_says,
254
266
  "" if nearly_said > 0 else " with includeHidden true",
@@ -271,13 +283,16 @@ func find_nodes(params: Dictionary) -> Dictionary:
271
283
  )
272
284
  if out_of_view > 0:
273
285
  answer["out_of_view"] = out_of_view
274
- notes.append(
275
- (
286
+ (
287
+ notes
288
+ . append(
276
289
  (
277
- "%d matching node%s drawn outside the viewport or scrolled out of a ScrollContainer"
278
- + " and left out; includeHidden true answers with them too"
290
+ (
291
+ "%d matching node%s drawn outside the viewport or scrolled out of a "
292
+ + "ScrollContainer and left out; includeHidden true answers with them too"
293
+ )
294
+ % [out_of_view, "" if out_of_view == 1 else "s"]
279
295
  )
280
- % [out_of_view, "" if out_of_view == 1 else "s"]
281
296
  )
282
297
  )
283
298
  if not notes.is_empty():
@@ -289,11 +304,11 @@ func find_nodes(params: Dictionary) -> Dictionary:
289
304
  ##
290
305
  ## A CanvasItem's visibility comes down through CanvasItem parents only, and one whose parent is not
291
306
  ## a CanvasItem starts again from its canvas: measured on 4.7.2, a Label under a plain Node under a
292
- ## hidden panel, and one under a hidden Node3D, both answer visible in tree, while one straight under
293
- ## the panel does not. Walking every ancestor called those two hidden, so their words were left off
294
- ## a screen that showed them and a wait on them ran out. What a CanvasItem's rule leaves out is its
295
- ## canvas: a hidden layer hides everything drawn on it while each item keeps its own flag, and a
296
- ## hidden window everything in it. A wait on words asks the same, so it reads this too.
307
+ ## hidden panel, and one under a hidden Node3D, both answer visible in tree, while one straight
308
+ ## under the panel does not. Walking every ancestor called those two hidden, so their words were
309
+ ## left off a screen that showed them and a wait on them ran out. What a CanvasItem's rule leaves
310
+ ## out is its canvas: a hidden layer hides everything drawn on it while each item keeps its own
311
+ ## flag, and a hidden window everything in it. A wait on words asks the same, so it reads this too.
297
312
  static func shown(node: Node) -> bool:
298
313
  var item: CanvasItem = node as CanvasItem
299
314
  if item != null:
@@ -355,9 +370,9 @@ func _found(node: Node, wanted_property: String) -> Dictionary:
355
370
  if wanted_property.is_empty():
356
371
  return entry
357
372
  entry["property"] = wanted_property
358
- # Through a path as well, for the reason [method GdhPaths.walk_to] gives. A step that is not there on
359
- # this node reads as not having the property rather than as a refusal, which is the rule this
360
- # whole function is written to: a find matches nodes of several classes on purpose.
373
+ # Through a path as well, for the reason [method GdhPaths.walk_to] gives. A step that is not
374
+ # there on this node reads as not having the property rather than as a refusal, which is the
375
+ # rule this whole function is written to: a find matches nodes of several classes on purpose.
361
376
  var reached: Dictionary = GdhPaths.walk_to(node, str(entry["path"]), wanted_property)
362
377
  if reached.has("message"):
363
378
  entry["has_property"] = false
@@ -463,7 +478,8 @@ func read_text(params: Dictionary) -> Dictionary:
463
478
  var limit: int = GdhRead.as_int(params.get("limit", READ_LIMIT), READ_LIMIT)
464
479
  if limit < 1 or limit > LIMIT_CEILING:
465
480
  return {
466
- "type": "error", "message": "limit is from 1 to %d, and %d was asked for" % [LIMIT_CEILING, limit]
481
+ "type": "error",
482
+ "message": "limit is from 1 to %d, and %d was asked for" % [LIMIT_CEILING, limit],
467
483
  }
468
484
 
469
485
  var reached: Dictionary = GdhValues.node_at(_host.get_tree().root, root_path)
@@ -477,7 +493,7 @@ func read_text(params: Dictionary) -> Dictionary:
477
493
  # and a bare `truncated: true` was looked straight past twice before the limit was suspected.
478
494
  # A number is what sends somebody back.
479
495
  var lines: Array[String] = []
480
- var tally: Dictionary[String, int] = {"omitted": 0, "out_of_view": 0, "copies": 0}
496
+ var tally: Dictionary[String, int] = { "omitted": 0, "out_of_view": 0, "copies": 0 }
481
497
  _read_into(root, include_hidden, limit, lines, tally)
482
498
  var answer: Dictionary = {
483
499
  "type": "text",
@@ -490,28 +506,35 @@ func read_text(params: Dictionary) -> Dictionary:
490
506
  var notes: Array[String] = []
491
507
  if tally["out_of_view"] > 0:
492
508
  answer["out_of_view"] = tally["out_of_view"]
493
- notes.append(
494
- (
509
+ (
510
+ notes
511
+ . append(
495
512
  (
496
- "%d line%s drawn outside the viewport or scrolled out of a ScrollContainer, where the"
497
- + " player cannot read them, left out; includeHidden true answers with them too"
513
+ (
514
+ "%d line%s drawn outside the viewport or scrolled out of a "
515
+ + "ScrollContainer, where the player cannot read them, left out; "
516
+ + "includeHidden true answers with them too"
517
+ )
518
+ % [tally["out_of_view"], "" if tally["out_of_view"] == 1 else "s"]
498
519
  )
499
- % [tally["out_of_view"], "" if tally["out_of_view"] == 1 else "s"]
500
520
  )
501
521
  )
502
522
  if tally["copies"] > 0:
503
523
  answer["copies"] = tally["copies"]
504
- notes.append(
505
- (
524
+ (
525
+ notes
526
+ . append(
506
527
  (
507
- "%d line%s drawn as a copy under a node saying the same words, as an outline or a glow"
508
- + " is drawn, read once with the words %s"
528
+ (
529
+ "%d line%s drawn as a copy under a node saying the same words, as an "
530
+ + "outline or a glow is drawn, read once with the words %s"
531
+ )
532
+ % [
533
+ tally["copies"],
534
+ "" if tally["copies"] == 1 else "s",
535
+ "it copies" if tally["copies"] == 1 else "they copy",
536
+ ]
509
537
  )
510
- % [
511
- tally["copies"],
512
- "" if tally["copies"] == 1 else "s",
513
- "it copies" if tally["copies"] == 1 else "they copy"
514
- ]
515
538
  )
516
539
  )
517
540
  if not notes.is_empty():
@@ -522,14 +545,14 @@ func read_text(params: Dictionary) -> Dictionary:
522
545
  ## Walks [param node] depth first, which is the order the screen is laid out in and the order a
523
546
  ## person reads it.
524
547
  ##
525
- ## Internal children as well, because the number in a SpinBox is one: the field a player reads it
526
- ## in is a LineEdit the engine builds inside the box and leaves out of `get_children()`, so a
527
- ## screen full of forms answered with every label on it and none of the values in it. The hidden
528
- ## check covers a [Window] for the same walk: a dropdown's popup is a child that is not drawn
529
- ## until it is opened, and reading a closed menu would put every item on the screen.
530
- ## Fills [param into] up to [param most] lines and counts in [param tally] how many further lines the
531
- ## rest of the subtree says, under omitted, and how many it says out of view, under out_of_view, which
532
- ## is what the caller is told rather than left to infer.
548
+ ## Internal children as well, because the number in a SpinBox is one: the field a player reads it in
549
+ ## is a LineEdit the engine builds inside the box and leaves out of `get_children()`, so a screen
550
+ ## full of forms answered with every label on it and none of the values in it. The hidden check
551
+ ## covers a [Window] for the same walk: a dropdown's popup is a child that is not drawn until it is
552
+ ## opened, and reading a closed menu would put every item on the screen. Fills [param into] up to
553
+ ## [param most] lines and counts in [param tally] how many further lines the rest of the subtree
554
+ ## says, under omitted, and how many it says out of view, under out_of_view, which is what the
555
+ ## caller is told rather than left to infer.
533
556
  ##
534
557
  ## Asked of each node rather than cut off at the first hidden one, since a hidden panel does not
535
558
  ## hide a label reached through a plain Node: see [method shown].
@@ -539,8 +562,8 @@ func _read_into(
539
562
  if include_hidden or shown(node):
540
563
  var said: Array[String] = GdhWords.lines_said_by(node)
541
564
  # A copy drawn under the words it copies, as an outline or a glow is, reads once. Only while
542
- # what it copies is itself read, so an original hidden or drawn out of view does not take the
543
- # words with it.
565
+ # what it copies is itself read, so an original hidden or drawn out of view does not take
566
+ # the words with it.
544
567
  var original: Node = GdhWords.copied_from(node) if not said.is_empty() else null
545
568
  if original != null and (include_hidden or readable(original)):
546
569
  tally["copies"] += said.size()
@@ -563,7 +586,7 @@ func _read_into(
563
586
  func get_rect(params: Dictionary) -> Dictionary:
564
587
  var node_path: String = str(params.get("path", ""))
565
588
  if node_path.is_empty():
566
- return {"type": "error", "message": "Node path required"}
589
+ return { "type": "error", "message": "Node path required" }
567
590
 
568
591
  var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
569
592
  if standing.has("message"):
@@ -613,14 +636,15 @@ func get_rect(params: Dictionary) -> Dictionary:
613
636
  var spatial: Node3D = node
614
637
  return _in_the_frame(node_path, spatial)
615
638
  return {
616
- "type": "error", "message": "%s is a %s, which has no place on screen" % [node_path, node.get_class()]
639
+ "type": "error",
640
+ "message": "%s is a %s, which has no place on screen" % [node_path, node.get_class()],
617
641
  }
618
642
 
619
643
 
620
644
  ## The rectangle a 2D node's own geometry takes up in its own coordinates, as the one entry of the
621
645
  ## list, which is empty for a node that has none of its own: a sprite's texture, a collision shape's
622
- ## shape, the points of a polygon or a line. A Node2D answered only where it stands, so the extent of
623
- ## a floor or a sprite to aim inside was a read of its shape and some arithmetic.
646
+ ## shape, the points of a polygon or a line. A Node2D answered only where it stands, so the extent
647
+ ## of a floor or a sprite to aim inside was a read of its shape and some arithmetic.
624
648
  static func _drawn_rect(item: Node2D) -> Array[Rect2]:
625
649
  var drawn: Array[Rect2] = []
626
650
  var sprite: Sprite2D = item as Sprite2D
@@ -668,7 +692,8 @@ func _in_the_frame(node_path: String, item: Node3D) -> Dictionary:
668
692
  if found.is_empty():
669
693
  return {
670
694
  "type": "error",
671
- "message": "%s is not in a viewport with a current Camera3D, so nothing is drawing it" % node_path
695
+ "message":
696
+ "%s is not in a viewport with a current Camera3D, so nothing is drawing it" % node_path,
672
697
  }
673
698
 
674
699
  var drawn_in: Viewport = item.get_viewport()
@@ -713,7 +738,9 @@ static func in_frame(item: Node3D) -> Dictionary:
713
738
  return {}
714
739
 
715
740
  var origin: Vector3 = item.global_transform.origin
716
- var found: Dictionary = {"camera": str(camera.get_path()), "behind": camera.is_position_behind(origin)}
741
+ var found: Dictionary = {
742
+ "camera": str(camera.get_path()), "behind": camera.is_position_behind(origin),
743
+ }
717
744
  var box: AABB = drawn_box(item)
718
745
  var draws: bool = box.size != Vector3.ZERO
719
746
  var middle: Vector3 = box.get_center() if draws else origin
@@ -740,7 +767,7 @@ static func _around(box: AABB, camera: Camera3D) -> Dictionary:
740
767
  return {}
741
768
  var point: Vector2 = camera.unproject_position(corner)
742
769
  seen = Rect2(point, Vector2.ZERO) if index == 0 else seen.expand(point)
743
- return {"rect": seen}
770
+ return { "rect": seen }
744
771
 
745
772
 
746
773
  ## The box everything drawn under [param item] fits in, in world space, or a box with no size when
@@ -770,7 +797,7 @@ func get_property(params: Dictionary) -> Dictionary:
770
797
  var property: String = str(params.get("property", ""))
771
798
 
772
799
  if node_path.is_empty() or property.is_empty():
773
- return {"type": "error", "message": "Node path and property required"}
800
+ return { "type": "error", "message": "Node path and property required" }
774
801
 
775
802
  var standing: Dictionary = GdhValues.target_at(_host.get_tree().root, node_path)
776
803
  if standing.has("message"):
@@ -786,7 +813,10 @@ func get_property(params: Dictionary) -> Dictionary:
786
813
  # Asked of the holder rather than read and compared to null, because a property the holder does
787
814
  # not have and a property that is null both read as null.
788
815
  if not GdhPaths.can_read(holder, named):
789
- return {"type": "error", "message": GdhPaths.nothing_there(holder, named, str(reached["called"]))}
816
+ return {
817
+ "type": "error",
818
+ "message": GdhPaths.nothing_there(holder, named, str(reached["called"])),
819
+ }
790
820
 
791
821
  return {
792
822
  "type": "property",
@@ -828,10 +858,10 @@ func get_metrics(params: Dictionary) -> Dictionary:
828
858
  var frame_time_max: Variant = null
829
859
  var physics_steps_per_second: Variant = null
830
860
  if timed > 0:
831
- frame_time = float(total_usec) / timed / 1000000.0
832
- frame_time_max = slowest_usec / 1000000.0
861
+ frame_time = float(total_usec) / timed / 1_000_000.0
862
+ frame_time_max = slowest_usec / 1_000_000.0
833
863
  if total_usec > 0:
834
- physics_steps_per_second = steps * 1000000.0 / total_usec
864
+ physics_steps_per_second = steps * 1_000_000.0 / total_usec
835
865
  var all: Dictionary = {
836
866
  "fps": Engine.get_frames_per_second(),
837
867
  "frame_time": frame_time,
@@ -854,9 +884,11 @@ func get_metrics(params: Dictionary) -> Dictionary:
854
884
  "object_node_count": Performance.get_monitor(Performance.OBJECT_NODE_COUNT),
855
885
  "object_orphan_node_count": Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT),
856
886
  "render_total_objects": Performance.get_monitor(Performance.RENDER_TOTAL_OBJECTS_IN_FRAME),
857
- "render_total_primitives": Performance.get_monitor(Performance.RENDER_TOTAL_PRIMITIVES_IN_FRAME),
858
- "render_total_draw_calls": Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME),
887
+ "render_total_primitives":
888
+ Performance.get_monitor(Performance.RENDER_TOTAL_PRIMITIVES_IN_FRAME),
889
+ "render_total_draw_calls":
859
890
  # The GPU's half of the game's memory, in bytes, which memory_static does not count: a game
891
+ Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME),
860
892
  # committing 2.3 GB answered 298 MB there, and the rest could not be asked about.
861
893
  "render_video_mem_used": Performance.get_monitor(Performance.RENDER_VIDEO_MEM_USED),
862
894
  "render_texture_mem_used": Performance.get_monitor(Performance.RENDER_TEXTURE_MEM_USED),
@@ -864,7 +896,7 @@ func get_metrics(params: Dictionary) -> Dictionary:
864
896
  }
865
897
 
866
898
  if metrics.is_empty():
867
- return {"type": "metrics", "data": all}
899
+ return { "type": "metrics", "data": all }
868
900
 
869
901
  # A caller that names metrics gets those and no others, and hears about a name that is
870
902
  # not one rather than getting everything back as if the list had not been sent.
@@ -878,9 +910,10 @@ func get_metrics(params: Dictionary) -> Dictionary:
878
910
  if not unknown.is_empty():
879
911
  return {
880
912
  "type": "error",
881
- "message": "Unknown metrics: %s. Available: %s" % [", ".join(unknown), ", ".join(all.keys())]
913
+ "message":
914
+ "Unknown metrics: %s. Available: %s" % [", ".join(unknown), ", ".join(all.keys())],
882
915
  }
883
- return {"type": "metrics", "data": selected}
916
+ return { "type": "metrics", "data": selected }
884
917
 
885
918
 
886
919
  func _serialize_node_tree(
@@ -897,7 +930,9 @@ func _serialize_node_tree(
897
930
  # it is wrong, and it is what sends somebody looking for another way to press the button.
898
931
  # `depth` is what keeps the answer a size worth reading.
899
932
  for child: Node in node.get_children(true):
900
- children.append(_serialize_node_tree(child, depth + 1, max_depth, include_properties, named))
933
+ children.append(
934
+ _serialize_node_tree(child, depth + 1, max_depth, include_properties, named)
935
+ )
901
936
  result["children"] = children
902
937
 
903
938
  return result
@@ -920,7 +955,9 @@ func _named_properties_of(node: Node, path: String, named: Array[String]) -> Dic
920
955
 
921
956
 
922
957
  func _serialize_node(node: Node, include_properties: bool) -> Dictionary:
923
- var result: Dictionary = {"name": node.name, "type": node.get_class(), "path": str(node.get_path())}
958
+ var result: Dictionary = {
959
+ "name": node.name, "type": node.get_class(), "path": str(node.get_path()),
960
+ }
924
961
 
925
962
  var script: Variant = node.get_script()
926
963
  if script is Script:
@@ -929,8 +966,9 @@ func _serialize_node(node: Node, include_properties: bool) -> Dictionary:
929
966
 
930
967
  if include_properties:
931
968
  var properties: Dictionary = {}
932
- # A script's own variables as well as what the node stores: a plain `var health` is not stored,
933
- # so a tree read of a running game answered every engine property and none of the game's state.
969
+ # A script's own variables as well as what the node stores: a plain `var health` is not
970
+ # stored, so a tree read of a running game answered every engine property and none of the
971
+ # game's state.
934
972
  for prop: Dictionary in node.get_property_list():
935
973
  if prop["usage"] & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_SCRIPT_VARIABLE):
936
974
  var property_name: String = prop["name"]
@@ -1,24 +1,19 @@
1
1
  extends RefCounted
2
-
3
2
  ## Whether a node says what a caller asked for in `says`: the one set of rules a find, a click and a
4
3
  ## wait all match words by, so the three never disagree about which control a word names.
5
4
 
6
5
  const GdhWords = preload("runtime_words.gd")
7
-
8
6
  ## Stands in for a bar written as `\|` while `says` is split into its alternatives: a character from
9
7
  ## the private use area, which no game's text uses.
10
8
  const ESCAPED_BAR: String = ""
11
-
12
- ## How many splits [method alternatives] keeps. A find asks about two at once, the words it was given
13
- ## and the same words widened, and anything past a handful is a caller that has moved on.
9
+ ## How many splits [method alternatives] keeps. A find asks about two at once, the words it was
10
+ ## given and the same words widened, and anything past a handful is a caller that has moved on.
14
11
  const SPLITS_KEPT: int = 8
15
12
 
16
13
  ## Each `says` split into alternatives, by what was written; see [method alternatives].
17
14
  static var _splits: Dictionary[String, Array] = {}
18
-
19
15
  ## Each `says` as the globs [method matches] tries first, by what was written; see [method _loose].
20
16
  static var _globs: Dictionary[String, Array] = {}
21
-
22
17
  ## Every space separator Unicode has besides the plain space, built once; see [method spaced].
23
18
  static var _spaces: RegEx = null
24
19
 
@@ -37,9 +32,9 @@ static func says(node: Node, wanted: String) -> bool:
37
32
  ## screen: twice in one session here, over a button that was.
38
33
  ##
39
34
  ## Several alternatives separated by `|` match when any one of them does, each by those rules: a
40
- ## wait for the end of a turn is a wait for whichever of "won", "lost" or the next turn's words comes
41
- ## first, and read as one glob with the bars in it, it waited out its whole timeout for words no
42
- ## screen says.
35
+ ## wait for the end of a turn is a wait for whichever of "won", "lost" or the next turn's words
36
+ ## comes first, and read as one glob with the bars in it, it waited out its whole timeout for words
37
+ ## no screen says.
43
38
  ##
44
39
  ## A space in the words matches any space separator in [param said], which is how they were typed.
45
40
  ## Each node's words are spaced only once a looser match says they may be the ones: this runs for
@@ -87,13 +82,13 @@ static func spaced(text: String) -> String:
87
82
  return text
88
83
  if _spaces == null:
89
84
  var separators: String = (
90
- String.chr(0x00A0)
85
+ String.chr(0x00a0)
91
86
  + String.chr(0x1680)
92
87
  + String.chr(0x2000)
93
88
  + "-"
94
- + String.chr(0x200A)
95
- + String.chr(0x202F)
96
- + String.chr(0x205F)
89
+ + String.chr(0x200a)
90
+ + String.chr(0x202f)
91
+ + String.chr(0x205f)
97
92
  + String.chr(0x3000)
98
93
  )
99
94
  _spaces = RegEx.create_from_string("[%s]" % separators)
@@ -194,13 +189,15 @@ static func point_on_words(label: RichTextLabel, wanted: String) -> Dictionary:
194
189
  start = at
195
190
  length = words.length()
196
191
  if start < 0:
197
- return {"unplaced": "the words were matched as a pattern, which names no one place in the text"}
192
+ return {
193
+ "unplaced": "the words were matched as a pattern, which names no one place in the text",
194
+ }
198
195
  if text.strip_edges().length() == length:
199
196
  return {}
200
197
  var line: int = label.get_character_line(start)
201
198
  var shown: Vector2i = label.get_line_range(line)
202
199
  if line < 0 or shown.x > start:
203
- return {"unplaced": "the label could not say which line the words are on"}
200
+ return { "unplaced": "the label could not say which line the words are on" }
204
201
  var on_line: int = mini(length, shown.y - start) if shown.y > start else length
205
202
  var font: Font = label.get_theme_font("normal_font")
206
203
  var font_size: int = label.get_theme_font_size("normal_font_size")
@@ -218,7 +215,7 @@ static func point_on_words(label: RichTextLabel, wanted: String) -> Dictionary:
218
215
  var scrolled: float = label.get_v_scroll_bar().value if label.scroll_active else 0.0
219
216
  var x: float = clampf(left + aligned + before + across * 0.5, left, left + room)
220
217
  var top: float = frame.get_margin(SIDE_TOP) + label.get_line_offset(line) - scrolled
221
- return {"point": Vector2(x, top + label.get_line_height(line) * 0.5)}
218
+ return { "point": Vector2(x, top + label.get_line_height(line) * 0.5) }
222
219
 
223
220
 
224
221
  ## How wide [param font] at [param font_size] draws [param words] on one line.
@@ -1,5 +1,4 @@
1
1
  extends RefCounted
2
-
3
2
  ## Where a point in some viewport is on the game's own screen, for a click to be sent there and for
4
3
  ## an answer to say where something is drawn.
5
4
 
@@ -28,24 +27,28 @@ static func reach(viewport: Viewport, point: Vector2) -> Dictionary:
28
27
  ## viewport's space, and the embedded [Window] or the [SubViewportContainer] in it that shows the
29
28
  ## step before, null for the first. What [method reach] answers is the last of them.
30
29
  static func steps_out(viewport: Viewport, point: Vector2) -> Array[Dictionary]:
31
- var out: Array[Dictionary] = [{"viewport": viewport, "point": point, "through": null}]
30
+ var out: Array[Dictionary] = [{ "viewport": viewport, "point": point, "through": null }]
32
31
  while true:
33
32
  var window: Window = viewport as Window
34
- var host: Viewport = embedder_of(window) if window != null and window.is_embedded() else null
33
+ var host: Viewport = (
34
+ embedder_of(window) if window != null and window.is_embedded() else null
35
+ )
35
36
  if host != null:
36
37
  # A window's canvas is drawn into its embedder through its own final transform, which is
37
- # where a content scale goes: one at 4 drew a button at four times the point it was clicked
38
- # at, and the click missed it.
38
+ # where a content scale goes: one at 4 drew a button at four times the point it was
39
+ # clicked at, and the click missed it.
39
40
  point = Vector2(window.position) + window.get_final_transform() * point
40
41
  viewport = host
41
- out.append({"viewport": viewport, "point": point, "through": window})
42
+ out.append({ "viewport": viewport, "point": point, "through": window })
42
43
  continue
43
44
  var shown_by: SubViewportContainer = viewport.get_parent() as SubViewportContainer
44
45
  if viewport is SubViewport and shown_by != null:
45
- var scaled: Vector2 = point * float(shown_by.stretch_shrink) if shown_by.stretch else point
46
+ var scaled: Vector2 = (
47
+ point * float(shown_by.stretch_shrink) if shown_by.stretch else point
48
+ )
46
49
  point = shown_by.get_global_transform_with_canvas() * scaled
47
50
  viewport = shown_by.get_viewport()
48
- out.append({"viewport": viewport, "point": point, "through": shown_by})
51
+ out.append({ "viewport": viewport, "point": point, "through": shown_by })
49
52
  continue
50
53
  break
51
54
  return out
@@ -101,7 +104,9 @@ static func shown_part(control: Control) -> Variant:
101
104
  while walking != null and not (walking is Viewport):
102
105
  var holder: ScrollContainer = walking as ScrollContainer
103
106
  if holder != null:
104
- clips.append(holder.get_global_transform_with_canvas() * Rect2(Vector2.ZERO, holder.size))
107
+ clips.append(
108
+ holder.get_global_transform_with_canvas() * Rect2(Vector2.ZERO, holder.size)
109
+ )
105
110
  walking = walking.get_parent()
106
111
  for clip: Rect2 in clips:
107
112
  if not part.intersects(clip, true):
@@ -125,7 +130,9 @@ static func clipped_by(control: Control, centre: Vector2) -> ScrollContainer:
125
130
  # Carried into the same space the centre is in, which is the canvas rather than the
126
131
  # container's own: the two are only the same while nothing above it is transformed.
127
132
  if holder != null:
128
- var seen: Rect2 = holder.get_global_transform_with_canvas() * Rect2(Vector2.ZERO, holder.size)
133
+ var seen: Rect2 = (
134
+ holder.get_global_transform_with_canvas() * Rect2(Vector2.ZERO, holder.size)
135
+ )
129
136
  if not seen.has_point(centre):
130
137
  return holder
131
138
  walking = walking.get_parent()
@@ -139,8 +146,8 @@ static func clipped_by(control: Control, centre: Vector2) -> ScrollContainer:
139
146
  ## next layout pass rather than inside the call, and the next one out works out where the control is
140
147
  ## from where it is drawn, so asked in the same frame it scrolled to where the control had been.
141
148
  ## Moved means a scroll value changed: a container asked to show what it already shows moves
142
- ## nothing, and answering true for it said the view had moved when it had not. Only the containers in
143
- ## the control's own viewport: one holding a dialog scrolls the page behind it, not the dialog.
149
+ ## nothing, and answering true for it said the view had moved when it had not. Only the containers
150
+ ## in the control's own viewport: one holding a dialog scrolls the page behind it, not the dialog.
144
151
  static func scroll_into_view(control: Control, tree: SceneTree) -> bool:
145
152
  var moved: bool = false
146
153
  var walking: Variant = control.get_parent()
@@ -179,17 +186,17 @@ static func through_the_interface(out: Array[Dictionary], hover: GdhHover) -> Di
179
186
  var window: Window = through
180
187
  var point: Vector2 = step["point"]
181
188
  if not Rect2(window.position, window.size).has_point(point):
182
- return {"landed": false, "hovered": _path_or_null(hovered)}
189
+ return { "landed": false, "hovered": _path_or_null(hovered) }
183
190
  continue
184
191
  if through is SubViewportContainer:
185
192
  if hovered != through:
186
- return {"landed": false, "hovered": _path_or_null(hovered)}
193
+ return { "landed": false, "hovered": _path_or_null(hovered) }
187
194
  continue
188
195
  if hovered != null and not _passes_on(hovered):
189
- return {"landed": false, "hovered": str(hovered.get_path())}
196
+ return { "landed": false, "hovered": str(hovered.get_path()) }
190
197
  if hovered != null and first_under == null:
191
198
  first_under = str(hovered.get_path())
192
- return {"landed": true, "hovered": first_under}
199
+ return { "landed": true, "hovered": first_under }
193
200
 
194
201
 
195
202
  ## Whether a pointer over [param control] goes on past the interface: a control that lets it pass