gdharness 1.3.0 → 1.3.2

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 (72) hide show
  1. package/build/cli.js +322 -96
  2. package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
  3. package/build/godot/addons/gdharness_editor/bridge_client.gd +8 -8
  4. package/build/godot/addons/gdharness_editor/declared_values.gd +116 -0
  5. package/build/godot/addons/gdharness_editor/declared_values.gd.uid +1 -0
  6. package/build/godot/addons/gdharness_editor/file_reads.gd +65 -25
  7. package/build/godot/addons/gdharness_editor/headless_check.gd +7 -7
  8. package/build/godot/addons/gdharness_editor/plugin.gd +6 -6
  9. package/build/godot/addons/gdharness_editor/property_values.gd +222 -59
  10. package/build/godot/addons/gdharness_editor/reading.gd +65 -0
  11. package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
  12. package/build/godot/addons/gdharness_editor/serialisation.gd +49 -94
  13. package/build/godot/addons/gdharness_editor/tool_executor.gd +24 -24
  14. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +52 -41
  15. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +4 -4
  16. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
  17. package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +14 -14
  18. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +175 -74
  19. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +17 -17
  20. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +14 -12
  21. package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +7 -7
  22. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +88 -84
  23. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
  24. package/build/godot/addons/gdharness_editor/value_shapes.gd +118 -0
  25. package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
  26. package/build/godot/addons/gdharness_runtime/reading.gd +65 -0
  27. package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
  28. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +29 -29
  29. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +21 -18
  30. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
  31. package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -64
  32. package/build/godot/addons/gdharness_runtime/runtime_items.gd +6 -6
  33. package/build/godot/addons/gdharness_runtime/runtime_menus.gd +8 -8
  34. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +6 -6
  35. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -10
  36. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +7 -7
  37. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +41 -41
  38. package/build/godot/addons/gdharness_runtime/runtime_says.gd +3 -3
  39. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +33 -33
  40. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +20 -20
  41. package/build/godot/addons/gdharness_runtime/runtime_values.gd +52 -25
  42. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
  43. package/build/godot/addons/gdharness_runtime/serialisation.gd +49 -94
  44. package/build/godot/addons/gdharness_runtime/value_shapes.gd +118 -0
  45. package/build/godot/addons/gdharness_runtime/value_shapes.gd.uid +1 -0
  46. package/build/godot/operations/animation_reads.gd +7 -19
  47. package/build/godot/operations/audio_buses.gd +10 -10
  48. package/build/godot/operations/class_cache.gd +16 -16
  49. package/build/godot/operations/classdb_queries.gd +25 -15
  50. package/build/godot/operations/declared_values.gd +116 -0
  51. package/build/godot/operations/dependencies.gd +141 -33
  52. package/build/godot/operations/editor_tools.gd +12 -12
  53. package/build/godot/operations/export_presets.gd +14 -14
  54. package/build/godot/operations/file_reads.gd +65 -25
  55. package/build/godot/operations/file_walk.gd +34 -0
  56. package/build/godot/operations/gdscript_analysis.gd +27 -22
  57. package/build/godot/operations/gdscript_authoring.gd +122 -17
  58. package/build/godot/operations/godot_operations.gd +66 -63
  59. package/build/godot/operations/import_pipeline.gd +59 -20
  60. package/build/godot/operations/input_actions.gd +90 -18
  61. package/build/godot/operations/logger.gd +1 -1
  62. package/build/godot/operations/plugins.gd +8 -8
  63. package/build/godot/operations/project_config.gd +83 -20
  64. package/build/godot/operations/project_diagnostics.gd +36 -11
  65. package/build/godot/operations/property_values.gd +222 -59
  66. package/build/godot/operations/reading.gd +65 -0
  67. package/build/godot/operations/rename_checks.gd +3 -3
  68. package/build/godot/operations/resource_files.gd +9 -6
  69. package/build/godot/operations/script_check.gd +23 -7
  70. package/build/godot/operations/serialisation.gd +49 -94
  71. package/build/godot/operations/value_shapes.gd +118 -0
  72. package/package.json +1 -1
@@ -4,12 +4,12 @@ extends RefCounted
4
4
  ## question, where one is on screen, a property's value, the metrics. What it changes, a property
5
5
  ## written or a method called, is in runtime_changes.gd.
6
6
 
7
- const Paths = preload("runtime_paths.gd")
8
- const Read = preload("reading.gd")
9
- const Says = preload("runtime_says.gd")
10
- const Screen = preload("runtime_screen.gd")
11
- const Values = preload("runtime_values.gd")
12
- const Words = preload("runtime_words.gd")
7
+ const GdhPaths = preload("runtime_paths.gd")
8
+ const GdhRead = preload("reading.gd")
9
+ const GdhSays = preload("runtime_says.gd")
10
+ const GdhScreen = preload("runtime_screen.gd")
11
+ const GdhValues = preload("runtime_values.gd")
12
+ const GdhWords = preload("runtime_words.gd")
13
13
 
14
14
  ## The most nodes one find answers with, unless asked for fewer: enough for any real query and
15
15
  ## far short of the tree dump a query exists to avoid.
@@ -32,7 +32,7 @@ const LIMIT_CEILING: int = 5000
32
32
  const FRAMES_TIMED: int = 120
33
33
 
34
34
  var _host: Node
35
- var _values: Values
35
+ var _values: GdhValues
36
36
  ## How long each of the last frames took, oldest first, in microseconds, as the game timed them.
37
37
  var _frame_usec: PackedInt64Array = PackedInt64Array()
38
38
  ## How many physics steps ran in each of those frames, in the same order.
@@ -43,7 +43,7 @@ var _last_physics_frames: int = 0
43
43
 
44
44
  ## The host is the autoload, which is how the tree is reached: it is not in one when the
45
45
  ## modules are built, and a fixture may put it in one later.
46
- func _init(host: Node, values: Values) -> void:
46
+ func _init(host: Node, values: GdhValues) -> void:
47
47
  _host = host
48
48
  _values = values
49
49
 
@@ -56,8 +56,8 @@ func _init(host: Node, values: Values) -> void:
56
56
  ## characters, and the same question as a find with one property answered two.
57
57
  func get_tree(params: Dictionary) -> Dictionary:
58
58
  var root_path: String = str(params.get("root", "/root"))
59
- var max_depth: int = Read.as_int(params.get("depth", 3), 3)
60
- var include_properties: bool = Read.as_bool(params.get("include_properties", false))
59
+ var max_depth: int = GdhRead.as_int(params.get("depth", 3), 3)
60
+ var include_properties: bool = GdhRead.as_bool(params.get("include_properties", false))
61
61
  var named: Array[String] = []
62
62
  var listed: Variant = params.get("properties", [])
63
63
  if listed is Array:
@@ -66,7 +66,7 @@ func get_tree(params: Dictionary) -> Dictionary:
66
66
  else:
67
67
  return {"type": "error", "message": "properties must be a list of property names"}
68
68
 
69
- var reached: Dictionary = Values.node_at(_host.get_tree().root, root_path)
69
+ var reached: Dictionary = GdhValues.node_at(_host.get_tree().root, root_path)
70
70
  if reached.has("message"):
71
71
  return reached
72
72
  var root: Node = reached["node"]
@@ -101,8 +101,8 @@ func find_nodes(params: Dictionary) -> Dictionary:
101
101
  for filter: String in FILTERS:
102
102
  wanted[filter] = str(params.get(filter, ""))
103
103
  var wanted_property: String = str(params.get("property", ""))
104
- var include_hidden: bool = Read.as_bool(params.get("include_hidden", true), true)
105
- var limit: int = Read.as_int(params.get("limit", FIND_LIMIT), FIND_LIMIT)
104
+ var include_hidden: bool = GdhRead.as_bool(params.get("include_hidden", true), true)
105
+ var limit: int = GdhRead.as_int(params.get("limit", FIND_LIMIT), FIND_LIMIT)
106
106
  if limit < 1 or limit > LIMIT_CEILING:
107
107
  return {
108
108
  "type": "error", "message": "limit is from 1 to %d, and %d was asked for" % [LIMIT_CEILING, limit]
@@ -114,12 +114,12 @@ func find_nodes(params: Dictionary) -> Dictionary:
114
114
  wanted["script"] = "res://" + wanted["script"]
115
115
 
116
116
  # Split once here rather than for each node the walk passes.
117
- var names: Array[String] = Says.split_at_bars(wanted["name"])
117
+ var names: Array[String] = GdhSays.split_at_bars(wanted["name"])
118
118
  if not wanted["name"].is_empty() and names.is_empty():
119
119
  var bars_only: String = 'name "%s" is only bars, so it names no node; write the names between them'
120
120
  return {"type": "error", "message": bars_only % wanted["name"]}
121
121
 
122
- var reached: Dictionary = Values.node_at(_host.get_tree().root, root_path)
122
+ var reached: Dictionary = GdhValues.node_at(_host.get_tree().root, root_path)
123
123
  if reached.has("message"):
124
124
  return reached
125
125
  var root: Node = reached["node"]
@@ -131,14 +131,14 @@ func find_nodes(params: Dictionary) -> Dictionary:
131
131
  # find would have matched if the name had been read the way it was probably meant.
132
132
  var plain_names: Array[String] = []
133
133
  for named: String in names:
134
- if Says.is_plain(named):
134
+ if GdhSays.is_plain(named):
135
135
  plain_names.append(named)
136
136
  var nearly: int = 0
137
137
  # The same for the words: a glob is matched against the whole text, and one written as a prefix
138
138
  # by habit, "Taken together*", missed the label whose sentence those words are in the middle of.
139
139
  # Nothing found then read as the words not being on screen, which is the wrong conclusion when
140
140
  # checking that a change to the text landed.
141
- var widened_says: String = Says.widened(wanted["says"])
141
+ var widened_says: String = GdhSays.widened(wanted["says"])
142
142
  var widened: Dictionary[String, String] = {}
143
143
  for filter: String in wanted:
144
144
  widened[filter] = widened_says if filter == "says" else wanted[filter]
@@ -192,7 +192,7 @@ func find_nodes(params: Dictionary) -> Dictionary:
192
192
  near_words.append('"%s"' % named)
193
193
  var opened: Array[String] = []
194
194
  for named: String in names:
195
- opened.append("*%s*" % named if Says.is_plain(named) else named)
195
+ opened.append("*%s*" % named if GdhSays.is_plain(named) else named)
196
196
  (
197
197
  notes
198
198
  . append(
@@ -203,7 +203,7 @@ func find_nodes(params: Dictionary) -> Dictionary:
203
203
  "" if nearly > 0 else "hidden ",
204
204
  holding,
205
205
  " or ".join(PackedStringArray(near_words)),
206
- Says.joined_at_bars(opened),
206
+ GdhSays.joined_at_bars(opened),
207
207
  "" if nearly > 0 else " with includeHidden true",
208
208
  ]
209
209
  )
@@ -304,19 +304,19 @@ func _found(node: Node, wanted_property: String) -> Dictionary:
304
304
  if wanted_property.is_empty():
305
305
  return entry
306
306
  entry["property"] = wanted_property
307
- # Through a path as well, for the reason [method Paths.walk_to] gives. A step that is not there on
307
+ # Through a path as well, for the reason [method GdhPaths.walk_to] gives. A step that is not there on
308
308
  # this node reads as not having the property rather than as a refusal, which is the rule this
309
309
  # whole function is written to: a find matches nodes of several classes on purpose.
310
- var reached: Dictionary = Paths.walk_to(node, str(entry["path"]), wanted_property)
310
+ var reached: Dictionary = GdhPaths.walk_to(node, str(entry["path"]), wanted_property)
311
311
  if reached.has("message"):
312
312
  entry["has_property"] = false
313
313
  return entry
314
314
  var holder: Variant = reached["holder"]
315
315
  var named: String = reached["name"]
316
- var has: bool = Paths.can_read(holder, named)
316
+ var has: bool = GdhPaths.can_read(holder, named)
317
317
  entry["has_property"] = has
318
318
  if has:
319
- entry["value"] = _values.serialize(Paths.read_under(holder, named))
319
+ entry["value"] = _values.serialize(GdhPaths.read_under(holder, named))
320
320
  return entry
321
321
 
322
322
 
@@ -356,7 +356,7 @@ func _matches_apart_from_name(node: Node, wanted: Dictionary[String, String]) ->
356
356
  # What this node says, rather than everything said underneath it. A row is then found by the
357
357
  # label in it, and the path answered is that label's, which is where the words a caller is
358
358
  # looking at actually are: matching every container above it would answer with the screen.
359
- if not wanted["says"].is_empty() and not Says.says(node, wanted["says"]):
359
+ if not wanted["says"].is_empty() and not GdhSays.says(node, wanted["says"]):
360
360
  return false
361
361
  var script: Variant = node.get_script()
362
362
  if not wanted["script"].is_empty():
@@ -408,14 +408,14 @@ static func _script_is(attached: Script, wanted: String) -> bool:
408
408
  ## whose interesting half is below the cut says so in a number rather than in a flag.
409
409
  func read_text(params: Dictionary) -> Dictionary:
410
410
  var root_path: String = str(params.get("root", "/root"))
411
- var include_hidden: bool = Read.as_bool(params.get("include_hidden", false))
412
- var limit: int = Read.as_int(params.get("limit", READ_LIMIT), READ_LIMIT)
411
+ var include_hidden: bool = GdhRead.as_bool(params.get("include_hidden", false))
412
+ var limit: int = GdhRead.as_int(params.get("limit", READ_LIMIT), READ_LIMIT)
413
413
  if limit < 1 or limit > LIMIT_CEILING:
414
414
  return {
415
415
  "type": "error", "message": "limit is from 1 to %d, and %d was asked for" % [LIMIT_CEILING, limit]
416
416
  }
417
417
 
418
- var reached: Dictionary = Values.node_at(_host.get_tree().root, root_path)
418
+ var reached: Dictionary = GdhValues.node_at(_host.get_tree().root, root_path)
419
419
  if reached.has("message"):
420
420
  return reached
421
421
  var root: Node = reached["node"]
@@ -453,7 +453,7 @@ func read_text(params: Dictionary) -> Dictionary:
453
453
  func _read_into(node: Node, include_hidden: bool, most: int, into: Array[String]) -> int:
454
454
  var left_out: int = 0
455
455
  if include_hidden or shown(node):
456
- for said: String in Words.lines_said_by(node):
456
+ for said: String in GdhWords.lines_said_by(node):
457
457
  if into.size() < most:
458
458
  into.append(said)
459
459
  else:
@@ -472,7 +472,7 @@ func get_rect(params: Dictionary) -> Dictionary:
472
472
  if node_path.is_empty():
473
473
  return {"type": "error", "message": "Node path required"}
474
474
 
475
- var standing: Dictionary = Values.node_at(_host.get_tree().root, node_path)
475
+ var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
476
476
  if standing.has("message"):
477
477
  return standing
478
478
  var node: Node = standing["node"]
@@ -491,7 +491,7 @@ func get_rect(params: Dictionary) -> Dictionary:
491
491
  "canvas": _values.serialize(canvas_rect),
492
492
  "window":
493
493
  _values.serialize(
494
- Screen.rect_in_window(
494
+ GdhScreen.rect_in_window(
495
495
  control.get_viewport(),
496
496
  control.get_global_transform_with_canvas() * Rect2(Vector2.ZERO, control.size)
497
497
  )
@@ -500,7 +500,7 @@ func get_rect(params: Dictionary) -> Dictionary:
500
500
  if node is Node2D:
501
501
  var item: Node2D = node
502
502
  var canvas_position: Vector2 = item.get_global_transform_with_canvas().origin
503
- var window_position: Vector2 = Screen.in_window(item.get_viewport(), canvas_position)
503
+ var window_position: Vector2 = GdhScreen.in_window(item.get_viewport(), canvas_position)
504
504
  return {
505
505
  "type": "point",
506
506
  "path": node_path,
@@ -544,12 +544,12 @@ func _in_the_frame(node_path: String, item: Node3D) -> Dictionary:
544
544
  if found.has("aim"):
545
545
  var aim: Vector2 = found["aim"]
546
546
  answer["canvas"] = _values.serialize(aim)
547
- answer["window"] = _values.serialize(Screen.in_window(drawn_in, aim))
547
+ answer["window"] = _values.serialize(GdhScreen.in_window(drawn_in, aim))
548
548
  if found.has("rect"):
549
549
  var covered: Rect2 = found["rect"]
550
550
  answer["covers"] = {
551
551
  "canvas": _values.serialize(covered),
552
- "window": _values.serialize(Screen.rect_in_window(drawn_in, covered)),
552
+ "window": _values.serialize(GdhScreen.rect_in_window(drawn_in, covered)),
553
553
  }
554
554
  return answer
555
555
 
@@ -634,12 +634,12 @@ func get_property(params: Dictionary) -> Dictionary:
634
634
  if node_path.is_empty() or property.is_empty():
635
635
  return {"type": "error", "message": "Node path and property required"}
636
636
 
637
- var standing: Dictionary = Values.target_at(_host.get_tree().root, node_path)
637
+ var standing: Dictionary = GdhValues.target_at(_host.get_tree().root, node_path)
638
638
  if standing.has("message"):
639
639
  return standing
640
640
  var target: Object = standing["object"]
641
641
 
642
- var reached: Dictionary = Paths.walk_to(target, node_path, property)
642
+ var reached: Dictionary = GdhPaths.walk_to(target, node_path, property)
643
643
  if reached.has("message"):
644
644
  return reached
645
645
 
@@ -647,14 +647,14 @@ func get_property(params: Dictionary) -> Dictionary:
647
647
  var named: String = reached["name"]
648
648
  # Asked of the holder rather than read and compared to null, because a property the holder does
649
649
  # not have and a property that is null both read as null.
650
- if not Paths.can_read(holder, named):
651
- return {"type": "error", "message": Paths.nothing_there(holder, named, str(reached["called"]))}
650
+ if not GdhPaths.can_read(holder, named):
651
+ return {"type": "error", "message": GdhPaths.nothing_there(holder, named, str(reached["called"]))}
652
652
 
653
653
  return {
654
654
  "type": "property",
655
655
  "path": node_path,
656
656
  "property": property,
657
- "value": _values.serialize(Paths.read_under(holder, named)),
657
+ "value": _values.serialize(GdhPaths.read_under(holder, named)),
658
658
  }
659
659
 
660
660
 
@@ -793,13 +793,13 @@ func _serialize_node_tree(
793
793
  func _named_properties_of(node: Node, path: String, named: Array[String]) -> Dictionary:
794
794
  var properties: Dictionary = {}
795
795
  for wanted: String in named:
796
- var reached: Dictionary = Paths.walk_to(node, path, wanted)
796
+ var reached: Dictionary = GdhPaths.walk_to(node, path, wanted)
797
797
  if reached.has("message"):
798
798
  continue
799
799
  var holder: Variant = reached["holder"]
800
800
  var name: String = reached["name"]
801
- if Paths.can_read(holder, name):
802
- properties[wanted] = _values.serialize(Paths.read_under(holder, name))
801
+ if GdhPaths.can_read(holder, name):
802
+ properties[wanted] = _values.serialize(GdhPaths.read_under(holder, name))
803
803
  return properties
804
804
 
805
805
 
@@ -3,7 +3,7 @@ extends RefCounted
3
3
  ## Whether a node says what a caller asked for in `says`: the one set of rules a find, a click and a
4
4
  ## wait all match words by, so the three never disagree about which control a word names.
5
5
 
6
- const Words = preload("runtime_words.gd")
6
+ const GdhWords = preload("runtime_words.gd")
7
7
 
8
8
  ## Stands in for a bar written as `\|` while `says` is split into its alternatives: a character from
9
9
  ## the private use area, which no game's text uses.
@@ -25,7 +25,7 @@ static var _spaces: RegEx = null
25
25
 
26
26
  ## Whether [param node]'s own words are what [param wanted] asks for.
27
27
  static func says(node: Node, wanted: String) -> bool:
28
- return matches(Words.said_by(node), wanted)
28
+ return matches(GdhWords.said_by(node), wanted)
29
29
 
30
30
 
31
31
  ## Whether [param said] is what [param wanted] asks for.
@@ -57,7 +57,7 @@ static func matches(said: String, wanted: String) -> bool:
57
57
 
58
58
 
59
59
  ## Each alternative of [param wanted] as a glob, open at both ends for words written plain, with
60
- ## every space as `?`. Words that match once spaced match this as written, since spacing changes
60
+ ## every space as `?`. GdhWords that match once spaced match this as written, since spacing changes
61
61
  ## only the characters a `?` stands for, so a text that does not is answered without being spaced.
62
62
  ## Kept by what was written, as [method alternatives] keeps its split.
63
63
  static func _loose(wanted: String) -> Array[String]:
@@ -2,13 +2,13 @@ extends RefCounted
2
2
 
3
3
  ## Which control a click means when it names the control by the words on it rather than by path.
4
4
 
5
- const Items = preload("runtime_items.gd")
6
- const Queries = preload("runtime_queries.gd")
7
- const Read = preload("reading.gd")
8
- const Says = preload("runtime_says.gd")
9
- const Screen = preload("runtime_screen.gd")
10
- const Values = preload("runtime_values.gd")
11
- const Words = preload("runtime_words.gd")
5
+ const GdhItems = preload("runtime_items.gd")
6
+ const GdhQueries = preload("runtime_queries.gd")
7
+ const GdhRead = preload("reading.gd")
8
+ const GdhSays = preload("runtime_says.gd")
9
+ const GdhScreen = preload("runtime_screen.gd")
10
+ const GdhValues = preload("runtime_values.gd")
11
+ const GdhWords = preload("runtime_words.gd")
12
12
 
13
13
  ## How many of the controls saying the same words a refusal names, with the index for each.
14
14
  const LISTED_CANDIDATES: int = 10
@@ -26,7 +26,7 @@ const WHOLE: int = 1
26
26
  ## The control a click by [param wanted] words presses, found under [param root_path] in the tree
27
27
  ## [param root] heads, or the refusal saying why there is none or which of several:
28
28
  ## {"path", "found"} or {"type", "message"}, with `item` as well when the match is an item of a
29
- ## control drawing a list, which [method Items.item_rect] places.
29
+ ## control drawing a list, which [method GdhItems.item_rect] places.
30
30
  ##
31
31
  ## Matched the way a find matches `says`, against a control's own words, and only among controls
32
32
  ## the player can see. A match inside a button presses the button, because the label is what says
@@ -45,7 +45,7 @@ const WHOLE: int = 1
45
45
  ## on screen for a player, and a click at it lands on the thing on top. The screen a game shows
46
46
  ## over its hall covers the hall's drawers without hiding them, so their words matched as well.
47
47
  static func control_saying(root: Node, root_path: String, wanted: String, which: Variant) -> Dictionary:
48
- var reached: Dictionary = Values.node_at(root, root_path)
48
+ var reached: Dictionary = GdhValues.node_at(root, root_path)
49
49
  if reached.has("message"):
50
50
  return reached
51
51
  var matched: Array[Control] = []
@@ -66,22 +66,22 @@ static func control_saying(root: Node, root_path: String, wanted: String, which:
66
66
  var pending: Array[Node] = [reached["node"]]
67
67
  while not pending.is_empty():
68
68
  var node: Node = pending.pop_front()
69
- if node is Control and Says.says(node, wanted):
70
- if not Queries.shown(node):
69
+ if node is Control and GdhSays.says(node, wanted):
70
+ if not GdhQueries.shown(node):
71
71
  hidden += 1
72
- if _says_exactly(Words.said_by(node), wanted):
72
+ if _says_exactly(GdhWords.said_by(node), wanted):
73
73
  exact_out_of_reach.append(node)
74
74
  out_of_reach_as.append("hidden")
75
- elif Words.draws_a_list(node):
75
+ elif GdhWords.draws_a_list(node):
76
76
  # Each item a match of its own, so the words of one tab are matched as a whole and the
77
77
  # index picks between two items saying them as it does between two buttons.
78
78
  var drawing: Control = node
79
- for entry: Dictionary in Words.items_of(drawing):
79
+ for entry: Dictionary in GdhWords.items_of(drawing):
80
80
  var words: String = entry["says"]
81
- if not Says.matches(words, wanted):
81
+ if not GdhSays.matches(words, wanted):
82
82
  continue
83
83
  var whole: bool = _says_exactly(words, wanted)
84
- var drawn_at_now: Rect2 = Items.item_rect(drawing, entry["item"], wanted)
84
+ var drawn_at_now: Rect2 = GdhItems.item_rect(drawing, entry["item"], wanted)
85
85
  matched.append(drawing)
86
86
  said.append(words)
87
87
  exact.append(whole)
@@ -95,7 +95,7 @@ static func control_saying(root: Node, root_path: String, wanted: String, which:
95
95
  else:
96
96
  var control: Control = node
97
97
  var target: Control = _pressed_through(control)
98
- var words: String = Words.said_by(node)
98
+ var words: String = GdhWords.said_by(node)
99
99
  var whole: bool = _says_exactly(words, wanted)
100
100
  var buried: bool = not whole and _inside_a_word(words, wanted)
101
101
  var at: int = matched.find(target)
@@ -193,7 +193,7 @@ static func control_saying(root: Node, root_path: String, wanted: String, which:
193
193
  % [_described(best, ranks[0], wanted, under), order, _candidates(targets, lines)]
194
194
  )
195
195
  }
196
- var index: int = 0 if which == null else Read.as_int(which, -1)
196
+ var index: int = 0 if which == null else GdhRead.as_int(which, -1)
197
197
  if index < 0 or index >= targets.size():
198
198
  # A number arrives from JSON as a float, and 5 was quoted back as 5.0.
199
199
  var given: String = str(index) if index >= 0 else str(which)
@@ -295,7 +295,7 @@ static func control_saying(root: Node, root_path: String, wanted: String, which:
295
295
  var answer: Dictionary = {"path": str(targets[index].get_path()), "found": found}
296
296
  if picked_items[index] != null:
297
297
  answer["item"] = picked_items[index]
298
- found["item"] = Items.item_named(targets[index], picked_items[index], lines[index])
298
+ found["item"] = GdhItems.item_named(targets[index], picked_items[index], lines[index])
299
299
  return answer
300
300
 
301
301
 
@@ -345,9 +345,9 @@ static func pointer_turned_off_by(control: Control) -> Control:
345
345
  ## named WARD, and read as its words run together it said WARD only as part of more, level with
346
346
  ## "WARDSPITE" over its own description, and a click on WARD was refused as not clear.
347
347
  static func _says_exactly(written: String, wanted: String) -> bool:
348
- var said: String = Says.spaced(written)
349
- for words: String in Says.alternatives(wanted):
350
- if not Says.is_plain(words):
348
+ var said: String = GdhSays.spaced(written)
349
+ for words: String in GdhSays.alternatives(wanted):
350
+ if not GdhSays.is_plain(words):
351
351
  if said.matchn(words):
352
352
  return true
353
353
  continue
@@ -364,11 +364,11 @@ static func _says_exactly(written: String, wanted: String) -> bool:
364
364
  ## never starting where a word starts: "out" in "Scout". Words starting a longer one count as said,
365
365
  ## "Recruit" in "Recruits", and so does anything matched by a pattern.
366
366
  static func _inside_a_word(written: String, wanted: String) -> bool:
367
- var said: String = Says.spaced(written).to_lower()
367
+ var said: String = GdhSays.spaced(written).to_lower()
368
368
  var found: bool = false
369
- for words: String in Says.alternatives(wanted):
369
+ for words: String in GdhSays.alternatives(wanted):
370
370
  var looked_for: String = words.to_lower()
371
- if not Says.is_plain(words) or not _in_a_word(looked_for.unicode_at(0)):
371
+ if not GdhSays.is_plain(words) or not _in_a_word(looked_for.unicode_at(0)):
372
372
  return false
373
373
  var at: int = said.find(looked_for)
374
374
  while at >= 0:
@@ -391,8 +391,8 @@ static func _described(count: int, rank: int, wanted: String, under: String) ->
391
391
  var what: String = "button" if rank & ENABLED_BUTTON else "control"
392
392
  var how: String = '"%s"' % wanted
393
393
  var plain: bool = true
394
- for words: String in Says.alternatives(wanted):
395
- plain = plain and Says.is_plain(words)
394
+ for words: String in GdhSays.alternatives(wanted):
395
+ plain = plain and GdhSays.is_plain(words)
396
396
  if plain:
397
397
  how = ('exactly "%s"' if rank & WHOLE else '"%s" as part of more') % wanted
398
398
  if count == 1:
@@ -756,7 +756,7 @@ static func _window_over(target: Control, point: Vector2, window: Window) -> boo
756
756
  if window.get_parent() == null or window.is_ancestor_of(target):
757
757
  return false
758
758
  var host: Viewport = window.get_parent().get_viewport()
759
- for step: Dictionary in Screen.steps_out(target.get_viewport(), point):
759
+ for step: Dictionary in GdhScreen.steps_out(target.get_viewport(), point):
760
760
  if step["viewport"] != host:
761
761
  continue
762
762
  var there: Vector2 = step["point"]
@@ -820,7 +820,7 @@ static func _off_screen(target: Control, aim: Vector2) -> bool:
820
820
  var point: Vector2 = target.get_global_transform_with_canvas() * aim + _moved_by(target, moves)
821
821
  if _clipped_away(target, point, moves) or not target.get_viewport().get_visible_rect().has_point(point):
822
822
  return true
823
- var reached: Dictionary = Screen.reach(target.get_viewport(), point)
823
+ var reached: Dictionary = GdhScreen.reach(target.get_viewport(), point)
824
824
  var viewport: Viewport = reached["viewport"]
825
825
  var there: Vector2 = reached["point"]
826
826
  return not viewport.get_visible_rect().has_point(there)
@@ -855,11 +855,11 @@ static func _clipped_away(
855
855
  ## words. A line in another font or holding an image measures off from what is drawn, which a wide
856
856
  ## line with nothing between the words and its start hardly does.
857
857
  static func point_on_words(label: RichTextLabel, wanted: String) -> Dictionary:
858
- var text: String = Says.spaced(Words.said_by(label))
858
+ var text: String = GdhSays.spaced(GdhWords.said_by(label))
859
859
  var start: int = -1
860
860
  var length: int = 0
861
- for words: String in Says.alternatives(wanted):
862
- if not Says.is_plain(words):
861
+ for words: String in GdhSays.alternatives(wanted):
862
+ if not GdhSays.is_plain(words):
863
863
  continue
864
864
  var at: int = text.findn(words)
865
865
  if at >= 0 and (start < 0 or at < start):
@@ -927,7 +927,7 @@ static func _open_menu_note(root: Node, wanted: String) -> String:
927
927
  if menu != null and menu.visible:
928
928
  for item: int in menu.item_count:
929
929
  var text: String = menu.get_item_text(item)
930
- if Says.matches(text, wanted):
930
+ if GdhSays.matches(text, wanted):
931
931
  var owner: Node = menu.get_parent()
932
932
  var named: Node = owner if owner is MenuButton or owner is OptionButton else menu
933
933
  return (
@@ -3,11 +3,11 @@ extends RefCounted
3
3
  ## What reaches the running game the way a keyboard reaches it: text typed into the field that has
4
4
  ## the focus, and an item chosen out of a menu the way a keyboard chooses one.
5
5
 
6
- const Menus = preload("runtime_menus.gd")
7
- const Read = preload("reading.gd")
8
- const Targets = preload("runtime_targets.gd")
9
- const Values = preload("runtime_values.gd")
10
- const Words = preload("runtime_words.gd")
6
+ const GdhMenus = preload("runtime_menus.gd")
7
+ const GdhRead = preload("reading.gd")
8
+ const GdhTargets = preload("runtime_targets.gd")
9
+ const GdhValues = preload("runtime_values.gd")
10
+ const GdhWords = preload("runtime_words.gd")
11
11
 
12
12
  ## The two characters a field reads as keys rather than as text.
13
13
  const NEWLINE: int = 10
@@ -41,7 +41,7 @@ func _init(host: Node) -> void:
41
41
  ## back now rather than echoed from the request, so a call that typed somewhere unhelpful says so.
42
42
  func inject_text(params: Dictionary) -> Dictionary:
43
43
  var text: String = str(params.get("text", ""))
44
- var over: bool = Read.as_bool(params.get("replace", false))
44
+ var over: bool = GdhRead.as_bool(params.get("replace", false))
45
45
  if text.is_empty() and not over:
46
46
  return {"type": "error", "message": "text needs something to type, or replace to empty a field"}
47
47
 
@@ -127,7 +127,7 @@ static func _select_everything(focused: Control) -> bool:
127
127
  static func _what_it_holds(focused: Control) -> Variant:
128
128
  var field: LineEdit = focused as LineEdit
129
129
  if field != null:
130
- return Words.masked(field) if field.secret else field.text
130
+ return GdhWords.masked(field) if field.secret else field.text
131
131
  var box: TextEdit = focused as TextEdit
132
132
  if box == null:
133
133
  return null
@@ -219,12 +219,12 @@ func choose(params: Dictionary) -> Dictionary:
219
219
  var node_path: String = str(params.get("path", ""))
220
220
  if node_path.is_empty():
221
221
  return {"type": "error", "message": "Node path required"}
222
- var standing: Dictionary = Values.node_at(_host.get_tree().root, node_path)
222
+ var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
223
223
  if standing.has("message"):
224
224
  return standing
225
225
  var node: Node = standing["node"]
226
226
 
227
- var menu: PopupMenu = Menus.menu_of(node)
227
+ var menu: PopupMenu = GdhMenus.menu_of(node)
228
228
  if menu == null:
229
229
  return {
230
230
  "type": "error",
@@ -241,7 +241,7 @@ func choose(params: Dictionary) -> Dictionary:
241
241
  "message":
242
242
  (
243
243
  "%s needs the item named, by text or index. It holds: %s"
244
- % [node_path, ", ".join(Menus.items_of(menu))]
244
+ % [node_path, ", ".join(GdhMenus.items_of(menu))]
245
245
  )
246
246
  }
247
247
 
@@ -255,7 +255,7 @@ func choose(params: Dictionary) -> Dictionary:
255
255
  return {
256
256
  "type": "error",
257
257
  "message":
258
- "%s is not visible%s, so its menu cannot be opened" % [node_path, Targets.why_hidden(holder)]
258
+ "%s is not visible%s, so its menu cannot be opened" % [node_path, GdhTargets.why_hidden(holder)]
259
259
  }
260
260
 
261
261
  # Shown first, because a menu nobody has opened has no focus to move and the press would go to
@@ -263,15 +263,15 @@ func choose(params: Dictionary) -> Dictionary:
263
263
  # a menu: one rebuilt on about_to_popup was read as it stood before, measured on 4.7.2, and the
264
264
  # index found in the old items chose another item of the new ones. An OptionButton opens its own;
265
265
  # a bare PopupMenu is popped where it already sits, which leaves a menu already open where it is.
266
- var opened: bool = Menus.open_the_menu(node, menu)
266
+ var opened: bool = GdhMenus.open_the_menu(node, menu)
267
267
  await _host.get_tree().process_frame
268
268
 
269
- var found: Array[int] = Menus.wanted_items(menu, params)
269
+ var found: Array[int] = GdhMenus.wanted_items(menu, params)
270
270
  if found.size() > 1:
271
271
  await _shut(menu, opened)
272
272
  var tied: Array[String] = []
273
273
  for at: int in found:
274
- tied.append("%d: %s" % [at, Words.item_says(menu, at)])
274
+ tied.append("%d: %s" % [at, GdhWords.item_says(menu, at)])
275
275
  return {
276
276
  "type": "error",
277
277
  "message":
@@ -294,7 +294,7 @@ func choose(params: Dictionary) -> Dictionary:
294
294
  "type": "chosen",
295
295
  "path": node_path,
296
296
  "index": index,
297
- "text": Words.item_says(menu, index),
297
+ "text": GdhWords.item_says(menu, index),
298
298
  "id": menu.get_item_id(index),
299
299
  "opened": opened,
300
300
  "menu": str(menu.get_path()),
@@ -329,13 +329,13 @@ func choose(params: Dictionary) -> Dictionary:
329
329
  )
330
330
  }
331
331
 
332
- answer["control_afterwards"] = Values.afterwards(node)
332
+ answer["control_afterwards"] = GdhValues.afterwards(node)
333
333
  # What the button in front of the menu reads now: a menu item that fired changes the thing
334
334
  # holding it. A button the pick took away has nothing to read, and that it went is the answer.
335
335
  if answer["control_afterwards"] == "in_tree" and node is OptionButton:
336
336
  var chooser: OptionButton = node
337
337
  answer["selected"] = chooser.get_selected()
338
- answer["shows"] = Words.said_by(chooser)
338
+ answer["shows"] = GdhWords.said_by(chooser)
339
339
  return answer
340
340
 
341
341
 
@@ -352,9 +352,9 @@ func _shut(menu: PopupMenu, opened: bool) -> void:
352
352
  ## Why item [param index] of [param menu] is not something to choose, or "" when it is.
353
353
  static func _not_a_choice(node_path: String, menu: PopupMenu, index: int) -> String:
354
354
  if index < 0:
355
- return "%s has no such item. It holds: %s" % [node_path, ", ".join(Menus.items_of(menu))]
355
+ return "%s has no such item. It holds: %s" % [node_path, ", ".join(GdhMenus.items_of(menu))]
356
356
  if menu.is_item_separator(index):
357
- var heading: String = Words.item_says(menu, index).strip_edges()
357
+ var heading: String = GdhWords.item_says(menu, index).strip_edges()
358
358
  if heading.is_empty():
359
359
  return "%s item %d is a separator, not a choice" % [node_path, index]
360
360
  return (
@@ -362,7 +362,7 @@ static func _not_a_choice(node_path: String, menu: PopupMenu, index: int) -> Str
362
362
  % [node_path, index, heading]
363
363
  )
364
364
  if menu.is_item_disabled(index):
365
- return "%s item %d, %s, is disabled" % [node_path, index, Words.item_says(menu, index)]
365
+ return "%s item %d, %s, is disabled" % [node_path, index, GdhWords.item_says(menu, index)]
366
366
  return ""
367
367
 
368
368