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
  ## The commands that take time: they let the game run and answer when what was waited for has
4
3
  ## happened, or when the time ran out, so the caller never sleeps for a guessed length.
5
4
 
@@ -10,31 +9,24 @@ const GdhQueries = preload("runtime_queries.gd")
10
9
  const GdhRead = preload("reading.gd")
11
10
  const GdhSays = preload("runtime_says.gd")
12
11
  const GdhValues = preload("runtime_values.gd")
13
-
14
12
  ## The longest one wait may last, whatever the request says: past this the server has long
15
13
  ## since given up on the reply.
16
14
  const CEILING_MSEC: int = 120000
17
-
18
15
  ## The most frames one wait may cover, which is about half a minute of a game drawing slowly.
19
16
  const MOST_FRAMES: int = 600
20
-
21
17
  ## How long a wait for words leaves between looks, as a multiple of what the last look took: nine
22
18
  ## gives looking about a tenth of the time.
23
19
  const LOOKS_APART: int = 9
24
-
25
20
  ## How a property wait compares what it reads with the value: equality by default, and order for a
26
21
  ## number that steps rather than lands, as a day counter on a fast clock goes from 31 to 34 between
27
22
  ## two looks and a wait for 32 never sees it.
28
23
  const COMPARES: Array[String] = ["==", "!=", ">=", "<=", ">", "<"]
29
-
30
24
  ## The most signal watches kept at once; arming one past this lets the oldest go. Property watches
31
25
  ## are counted apart, to the same number.
32
26
  const MOST_WATCHES: int = 16
33
-
34
27
  ## The most changes a property watch keeps. Past it, each new one takes the last place, so what is
35
28
  ## kept still ends on the latest value.
36
29
  const MOST_CHANGES: int = 1000
37
-
38
30
  ## The commands that can change the game, by the tool call that sends each, which a property watch
39
31
  ## notes as they arrive.
40
32
  const CALLS_NAMED: Dictionary[String, String] = {
@@ -51,10 +43,9 @@ const CALLS_NAMED: Dictionary[String, String] = {
51
43
  "inject_joypad_button": "runtime_input joypad_button",
52
44
  "inject_joypad_motion": "runtime_input joypad_motion",
53
45
  }
54
-
55
46
  ## What a call noted by a property watch carries of its parameters, when it was given them.
56
47
  const CALL_FIELDS: Array[String] = [
57
- "path", "method", "property", "action", "keycode", "button", "pressed", "says"
48
+ "path", "method", "property", "action", "keycode", "button", "pressed", "says",
58
49
  ]
59
50
 
60
51
  var _host: Node
@@ -76,7 +67,8 @@ func _init(host: Node, values: GdhValues) -> void:
76
67
  ## frames of the game having passed, and everything measured off it is out by that much.
77
68
  static func _out_of_range(named: String, asked: int, least: int, most: int) -> Dictionary:
78
69
  return {
79
- "type": "error", "message": "%s is %d, and %s takes %d to %d." % [named, asked, named, least, most]
70
+ "type": "error",
71
+ "message": "%s is %d, and %s takes %d to %d." % [named, asked, named, least, most],
80
72
  }
81
73
 
82
74
 
@@ -84,7 +76,10 @@ static func _out_of_range(named: String, asked: int, least: int, most: int) -> D
84
76
  ## or every one when none is near: a name to send instead.
85
77
  static func _no_signal(node: Node, node_path: String, signal_name: String) -> Dictionary:
86
78
  var said: String = "%s has no signal %s" % [node_path, signal_name]
87
- return {"type": "error", "message": GdhRead.offered(said, signal_name, GdhRead.signals_of(node)) + "."}
79
+ return {
80
+ "type": "error",
81
+ "message": GdhRead.offered(said, signal_name, GdhRead.signals_of(node)) + ".",
82
+ }
88
83
 
89
84
 
90
85
  func wait_frames(params: Dictionary) -> Dictionary:
@@ -94,7 +89,7 @@ func wait_frames(params: Dictionary) -> Dictionary:
94
89
  var started: int = Time.get_ticks_msec()
95
90
  for _frame: int in frames:
96
91
  await _host.get_tree().process_frame
97
- return {"type": "waited", "frames": frames, "elapsed_ms": Time.get_ticks_msec() - started}
92
+ return { "type": "waited", "frames": frames, "elapsed_ms": Time.get_ticks_msec() - started }
98
93
 
99
94
 
100
95
  ## Waits for a signal to fire, and answers with what it carried, or that the time ran out. Given a
@@ -108,7 +103,7 @@ func wait_signal(params: Dictionary) -> Dictionary:
108
103
  if params.has("watch"):
109
104
  return await _collect(GdhRead.as_int(params["watch"], 0), timeout_ms)
110
105
  if node_path.is_empty() or signal_name.is_empty():
111
- return {"type": "error", "message": "Node path and signal name required"}
106
+ return { "type": "error", "message": "Node path and signal name required" }
112
107
 
113
108
  var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
114
109
  if standing.has("message"):
@@ -121,7 +116,7 @@ func wait_signal(params: Dictionary) -> Dictionary:
121
116
  var callable: Callable = catcher._on_fired
122
117
  var listening: Error = node.connect(signal_name, callable, CONNECT_ONE_SHOT)
123
118
  if listening != OK:
124
- return {"type": "error", "message": "Could not listen to signal: " + signal_name}
119
+ return { "type": "error", "message": "Could not listen to signal: " + signal_name }
125
120
  var started: int = Time.get_ticks_msec()
126
121
  while not catcher.fired and Time.get_ticks_msec() - started < timeout_ms:
127
122
  await _host.get_tree().process_frame
@@ -138,10 +133,10 @@ func wait_signal(params: Dictionary) -> Dictionary:
138
133
  }
139
134
  if not catcher.fired:
140
135
  answer["note"] = (
141
- "A wait hears the signal only from when it begins, so one that fired before it is not"
142
- + " counted. If a call meant to set the signal off was sent beside this wait, it may have run"
143
- + " only after the wait answered, as a client can run its calls in turn: arm runtime_wait"
144
- + " watch first, make the call, then wait with signal and that watch"
136
+ "A wait hears the signal only from when it begins, so one that fired before it is not "
137
+ + "counted. If a call meant to set the signal off was sent beside this wait, it may "
138
+ + "have run only after the wait answered, as a client can run its calls in turn: arm "
139
+ + "runtime_wait watch first, make the call, then wait with signal and that watch"
145
140
  )
146
141
  return answer
147
142
 
@@ -149,8 +144,9 @@ func wait_signal(params: Dictionary) -> Dictionary:
149
144
  ## Starts recording a signal and answers at once with the watch to collect it by.
150
145
  ##
151
146
  ## A wait sent beside the call meant to set its signal off does not see it when the client runs the
152
- ## two in turn: Claude Code ran a key press only after the signal wait beside it had answered, so the
153
- ## wait ran out and the press landed after it. A watch is armed before the call and collected after.
147
+ ## two in turn: Claude Code ran a key press only after the signal wait beside it had answered, so
148
+ ## the wait ran out and the press landed after it. A watch is armed before the call and collected
149
+ ## after.
154
150
  func watch_signal(params: Dictionary) -> Dictionary:
155
151
  var node_path: String = str(params.get("path", ""))
156
152
  var signal_name: String = str(params.get("signal", ""))
@@ -160,14 +156,17 @@ func watch_signal(params: Dictionary) -> Dictionary:
160
156
  "type": "error",
161
157
  "message":
162
158
  (
163
- "A watch records a signal or a property, not both: this one has signal %s and property %s"
159
+ (
160
+ "A watch records a signal or a property, not both: this one has signal %s and "
161
+ + "property %s"
162
+ )
164
163
  % [signal_name, property]
165
- )
164
+ ),
166
165
  }
167
166
  if not property.is_empty():
168
167
  return _watch_property(node_path, property)
169
168
  if node_path.is_empty() or signal_name.is_empty():
170
- return {"type": "error", "message": "Node path and signal name required"}
169
+ return { "type": "error", "message": "Node path and signal name required" }
171
170
  var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
172
171
  if standing.has("message"):
173
172
  return standing
@@ -177,14 +176,14 @@ func watch_signal(params: Dictionary) -> Dictionary:
177
176
  var watch: GdhWatch = GdhWatch.new(node, node_path, signal_name)
178
177
  var listening: Error = node.connect(signal_name, watch.catcher._on_fired)
179
178
  if listening != OK:
180
- return {"type": "error", "message": "Could not listen to signal: " + signal_name}
179
+ return { "type": "error", "message": "Could not listen to signal: " + signal_name }
181
180
  while _watches.size() >= MOST_WATCHES:
182
181
  var oldest: int = _watches.keys().min()
183
182
  _let_go(oldest)
184
183
  var id: int = _next_watch
185
184
  _next_watch += 1
186
185
  _watches[id] = watch
187
- return {"type": "watching", "watch": id, "path": node_path, "signal": signal_name}
186
+ return { "type": "watching", "watch": id, "path": node_path, "signal": signal_name }
188
187
 
189
188
 
190
189
  ## Answers at once with the first emission recorded since watch [param id] was armed, otherwise
@@ -197,7 +196,7 @@ func _collect(id: int, timeout_ms: int) -> Dictionary:
197
196
  (
198
197
  "Watch %d records %s:%s, a property, and is collected with runtime_wait recorded"
199
198
  % [id, _traces[id].node_path, _traces[id].property]
200
- )
199
+ ),
201
200
  }
202
201
  if not _watches.has(id):
203
202
  return {
@@ -205,8 +204,8 @@ func _collect(id: int, timeout_ms: int) -> Dictionary:
205
204
  "message":
206
205
  (
207
206
  (
208
- "No watch %d is armed in this game: it was collected already, %d armed after it let it"
209
- + " go, or the game started again since"
207
+ "No watch %d is armed in this game: it was collected already, %d armed after "
208
+ + "it let it go, or the game started again since"
210
209
  )
211
210
  % [id, MOST_WATCHES]
212
211
  ),
@@ -254,7 +253,7 @@ func _let_go(id: int) -> void:
254
253
  ## only by slowing the game's clock. Armed before the click, the watch has every frame of it.
255
254
  func _watch_property(node_path: String, property: String) -> Dictionary:
256
255
  if node_path.is_empty():
257
- return {"type": "error", "message": "Node path required"}
256
+ return { "type": "error", "message": "Node path required" }
258
257
  var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
259
258
  if standing.has("message"):
260
259
  return standing
@@ -270,7 +269,8 @@ func _watch_property(node_path: String, property: String) -> Dictionary:
270
269
  _next_watch += 1
271
270
  _traces[id] = trace
272
271
  # Left to run across frames while the answer goes now. Deferred calls run once the frame's
273
- # _process is done and before its tweens step, so the recording starts in the frame it was armed in.
272
+ # _process is done and before its tweens step, so the recording starts in the frame it was armed
273
+ # in.
274
274
  _record.call_deferred(trace)
275
275
  return {
276
276
  "type": "watching",
@@ -303,7 +303,9 @@ func _record(trace: GdhTrace) -> void:
303
303
  var held: Object = trace.node.get_ref()
304
304
  var node: Node = held as Node
305
305
  if node == null:
306
- trace.ended = "%s was freed %d frames after the watch was armed" % [trace.node_path, frame]
306
+ trace.ended = (
307
+ "%s was freed %d frames after the watch was armed" % [trace.node_path, frame]
308
+ )
307
309
  return
308
310
  var watched: Dictionary = _watched(node, trace.node_path, trace.property)
309
311
  if watched.has("message"):
@@ -314,7 +316,8 @@ func _record(trace: GdhTrace) -> void:
314
316
  trace.elapsed_ms = elapsed
315
317
  if elapsed >= CEILING_MSEC:
316
318
  trace.ended = (
317
- "recording stopped %d ms after the watch was armed, the longest a wait lasts" % elapsed
319
+ "recording stopped %d ms after the watch was armed, the longest a wait lasts"
320
+ % elapsed
318
321
  )
319
322
  return
320
323
 
@@ -333,7 +336,7 @@ func saw_call(command: String, params: Dictionary) -> void:
333
336
  if trace.calls.size() >= MOST_CHANGES:
334
337
  trace.calls_dropped += 1
335
338
  continue
336
- var noted: Dictionary = {"frame": trace.frames, "call": named}
339
+ var noted: Dictionary = { "frame": trace.frames, "call": named }
337
340
  for field: String in CALL_FIELDS:
338
341
  if params.has(field):
339
342
  noted[field] = params[field]
@@ -351,7 +354,7 @@ func recorded(params: Dictionary) -> Dictionary:
351
354
  (
352
355
  "Watch %d records the signal %s of %s, and is collected with runtime_wait signal"
353
356
  % [id, _watches[id].signal_name, _watches[id].node_path]
354
- )
357
+ ),
355
358
  }
356
359
  if not _traces.has(id):
357
360
  return {
@@ -359,8 +362,8 @@ func recorded(params: Dictionary) -> Dictionary:
359
362
  "message":
360
363
  (
361
364
  (
362
- "No property watch %d is armed in this game: it was collected already, %d armed after"
363
- + " it let it go, or the game started again since"
365
+ "No property watch %d is armed in this game: it was collected already, %d "
366
+ + "armed after it let it go, or the game started again since"
364
367
  )
365
368
  % [id, MOST_WATCHES]
366
369
  ),
@@ -383,8 +386,8 @@ func recorded(params: Dictionary) -> Dictionary:
383
386
  if trace.dropped > 0:
384
387
  answer["dropped"] = (
385
388
  (
386
- "%d changes are left out: past %d, each new one took the last place, so the list ends on"
387
- + " the latest"
389
+ "%d changes are left out: past %d, each new one took the last place, so the list "
390
+ + "ends on the latest"
388
391
  )
389
392
  % [trace.dropped, MOST_CHANGES]
390
393
  )
@@ -403,7 +406,7 @@ func wait_until(params: Dictionary) -> Dictionary:
403
406
  if timeout_ms < 1 or timeout_ms > CEILING_MSEC:
404
407
  return _out_of_range("timeout_ms", timeout_ms, 1, CEILING_MSEC)
405
408
  if node_path.is_empty():
406
- return {"type": "error", "message": "Node path required"}
409
+ return { "type": "error", "message": "Node path required" }
407
410
  var standing: Dictionary = GdhValues.node_at(_host.get_tree().root, node_path)
408
411
  if standing.has("message"):
409
412
  return standing
@@ -411,7 +414,10 @@ func wait_until(params: Dictionary) -> Dictionary:
411
414
  # path, a property compares one value on one node. Taking says and dropping the other left a
412
415
  # caller watching a screen while believing they were watching a property, and the answer says
413
416
  # which of them it is about only if you already know that says wins.
414
- if not says.is_empty() and (not property.is_empty() or params.has("value") or params.has("compare")):
417
+ if (
418
+ not says.is_empty()
419
+ and (not property.is_empty() or params.has("value") or params.has("compare"))
420
+ ):
415
421
  var other: String = (
416
422
  property if not property.is_empty() else ("value" if params.has("value") else "compare")
417
423
  )
@@ -420,18 +426,18 @@ func wait_until(params: Dictionary) -> Dictionary:
420
426
  "message":
421
427
  (
422
428
  (
423
- 'A wait takes says or a property, not both: this one has says "%s" and %s as well.'
424
- + " Ask for one of them."
429
+ 'A wait takes says or a property, not both: this one has says "%s" and %s as '
430
+ + "well. Ask for one of them."
425
431
  )
426
432
  % [says, other]
427
- )
433
+ ),
428
434
  }
429
435
  if not says.is_empty():
430
436
  var include_hidden: bool = GdhRead.as_bool(params.get("include_hidden", false))
431
437
  return await _wait_until_said(node_path, says, timeout_ms, include_hidden)
432
438
  var unasked: String = _property_wait_unasked(property, params)
433
439
  if not unasked.is_empty():
434
- return {"type": "error", "message": unasked}
440
+ return { "type": "error", "message": unasked }
435
441
  var compare: String = str(params.get("compare", "=="))
436
442
 
437
443
  var node: Node = standing["node"]
@@ -451,13 +457,15 @@ func wait_until(params: Dictionary) -> Dictionary:
451
457
  var sent: Variant = by_name[1] if not by_name.is_empty() and by_name[0] else params["value"]
452
458
  var wanted: Variant = _values.fitted(sent, typeof(current))
453
459
  # Read against what the property is declared as, not only against what it holds this frame: an
454
- # object property holding null holds nothing typed, so any value was taken as it came and compared
455
- # with null until the time ran out. #772 waited a minute for "null" that way and answered met
456
- # false with value null beside it.
460
+ # object property holding null holds nothing typed, so any value was taken as it came and
461
+ # compared with null until the time ran out. #772 waited a minute for "null" that way and
462
+ # answered met false with value null beside it.
457
463
  var declared: int = watched["declared"]
458
464
  # An untyped slot holding null or an object is read the same way, as set reads it: otherwise the
459
465
  # two ops would disagree about one property, and the wait would run out on the text null.
460
- var untyped_object: bool = declared == TYPE_NIL and (current == null or typeof(current) == TYPE_OBJECT)
466
+ var untyped_object: bool = (
467
+ declared == TYPE_NIL and (current == null or typeof(current) == TYPE_OBJECT)
468
+ )
461
469
  var holds_objects: bool = declared == TYPE_OBJECT or untyped_object
462
470
  if holds_objects and GdhChanges.names_no_object(wanted):
463
471
  wanted = null
@@ -480,15 +488,15 @@ func wait_until(params: Dictionary) -> Dictionary:
480
488
  % [node_path, property, type_string(typeof(wanted))]
481
489
  )
482
490
  if not refused.is_empty():
483
- return {"type": "error", "message": refused}
491
+ return { "type": "error", "message": refused }
484
492
 
485
493
  var started: int = Time.get_ticks_msec()
486
494
  var frames: int = 0
487
495
  # The type is checked every time round, not only at the top: a property that holds an object a
488
- # frame later is the same error arriving late.
489
- # At the precision the property keeps, as set compares: a float arrives as 64 bits and most
490
- # engine properties keep 32, so h_offset waited on as -1.1 read -1.10000002384186 every frame and
491
- # the wait ran out on a value the game had reached.
496
+ # frame later is the same error arriving late. At the precision the property keeps, as set
497
+ # compares: a float arrives as 64 bits and most engine properties keep 32, so h_offset waited on
498
+ # as -1.1 read -1.10000002384186 every frame and the wait ran out on a value the game had
499
+ # reached.
492
500
  var differs: bool = compare == "!="
493
501
  while (
494
502
  GdhValues.comparable(current, wanted)
@@ -498,7 +506,7 @@ func wait_until(params: Dictionary) -> Dictionary:
498
506
  await _host.get_tree().process_frame
499
507
  frames += 1
500
508
  if not is_instance_valid(node):
501
- return {"type": "error", "message": "%s was freed while waiting" % node_path}
509
+ return { "type": "error", "message": "%s was freed while waiting" % node_path }
502
510
  watched = _watched(node, node_path, property)
503
511
  if watched.has("message"):
504
512
  return watched
@@ -509,13 +517,18 @@ func wait_until(params: Dictionary) -> Dictionary:
509
517
  "path": node_path,
510
518
  "property": property,
511
519
  "compare": compare,
512
- "met": GdhValues.comparable(current, wanted) and GdhChanges.held_already(wanted, current) != differs,
520
+ "met":
521
+ (
522
+ GdhValues.comparable(current, wanted)
523
+ and GdhChanges.held_already(wanted, current) != differs
524
+ ),
513
525
  "value": _values.serialize(current),
514
526
  "elapsed_ms": Time.get_ticks_msec() - started,
515
527
  "frames": frames,
516
528
  }
517
529
  # Said rather than refused, since a game may store a number no value has, as set says it stores
518
- # one: a wait for 7 on an enum of three ran its whole timeout and answered only that it was not met.
530
+ # one: a wait for 7 on an enum of three ran its whole timeout and answered only that it was not
531
+ # met.
519
532
  if watched["holder"] is Object:
520
533
  var owner: Object = watched["holder"]
521
534
  var shown: String = JSON.stringify(_values.serialize(wanted), "", false)
@@ -542,8 +555,11 @@ static func _property_wait_unasked(property: String, params: Dictionary) -> Stri
542
555
  ## is past the value, or level with it at the precision the property keeps, as `==` compares.
543
556
  ##
544
557
  ## The value is read as the number it is rather than fitted to the property's type, since fitting
545
- ## truncates: 31.5 on a whole-number property became 31, and a wait for "at least 31.5" was met at 31.
546
- func _wait_in_order(node: Node, params: Dictionary, current: Variant, timeout_ms: int) -> Dictionary:
558
+ ## truncates: 31.5 on a whole-number property became 31, and a wait for "at least 31.5" was met at
559
+ ## 31.
560
+ func _wait_in_order(
561
+ node: Node, params: Dictionary, current: Variant, timeout_ms: int
562
+ ) -> Dictionary:
547
563
  var node_path: String = str(params.get("path", ""))
548
564
  var property: String = str(params.get("property", ""))
549
565
  var compare: String = str(params.get("compare", ""))
@@ -552,7 +568,7 @@ func _wait_in_order(node: Node, params: Dictionary, current: Variant, timeout_ms
552
568
  # came with it, since no number would make the wait answerable.
553
569
  var unordered: String = _no_order(current, node_path, property, compare)
554
570
  if not unordered.is_empty():
555
- return {"type": "error", "message": unordered}
571
+ return { "type": "error", "message": unordered }
556
572
  var number: Variant = given
557
573
  if given is String:
558
574
  var text: String = given
@@ -564,7 +580,7 @@ func _wait_in_order(node: Node, params: Dictionary, current: Variant, timeout_ms
564
580
  (
565
581
  "compare %s puts a number in order, and the value to wait for is %s: give a number."
566
582
  % [compare, str(given)]
567
- )
583
+ ),
568
584
  }
569
585
  var limit: float = GdhRead.as_float(number)
570
586
 
@@ -574,14 +590,14 @@ func _wait_in_order(node: Node, params: Dictionary, current: Variant, timeout_ms
574
590
  await _host.get_tree().process_frame
575
591
  frames += 1
576
592
  if not is_instance_valid(node):
577
- return {"type": "error", "message": "%s was freed while waiting" % node_path}
593
+ return { "type": "error", "message": "%s was freed while waiting" % node_path }
578
594
  var watched: Dictionary = _watched(node, node_path, property)
579
595
  if watched.has("message"):
580
596
  return watched
581
597
  current = watched["value"]
582
598
  unordered = _no_order(current, node_path, property, compare)
583
599
  if not unordered.is_empty():
584
- return {"type": "error", "message": unordered}
600
+ return { "type": "error", "message": unordered }
585
601
 
586
602
  return {
587
603
  "type": "condition",
@@ -596,7 +612,9 @@ func _wait_in_order(node: Node, params: Dictionary, current: Variant, timeout_ms
596
612
 
597
613
 
598
614
  ## Why [param current] cannot be put in order, or "" when it is a number.
599
- static func _no_order(current: Variant, node_path: String, property: String, compare: String) -> String:
615
+ static func _no_order(
616
+ current: Variant, node_path: String, property: String, compare: String
617
+ ) -> String:
600
618
  if typeof(current) == TYPE_INT or typeof(current) == TYPE_FLOAT:
601
619
  return ""
602
620
  return (
@@ -610,7 +628,8 @@ static func _no_order(current: Variant, node_path: String, property: String, com
610
628
  static func _in_order(compare: String, limit: float, current: Variant) -> bool:
611
629
  var reading: float = GdhRead.as_float(current)
612
630
  var level: bool = (
613
- reading == limit or (typeof(current) == TYPE_FLOAT and GdhChanges.held_already(limit, current))
631
+ reading == limit
632
+ or (typeof(current) == TYPE_FLOAT and GdhChanges.held_already(limit, current))
614
633
  )
615
634
  match compare:
616
635
  ">=":
@@ -633,8 +652,8 @@ static func _cut_to_fit(given: Variant, type: int, node_path: String, property:
633
652
  return ""
634
653
  return (
635
654
  (
636
- "%s.%s holds %s and the value to wait for is %s, which %s cannot hold without changing it:"
637
- + " waiting on it would answer about a state nobody asked for."
655
+ "%s.%s holds %s and the value to wait for is %s, which %s cannot hold without changing "
656
+ + "it: waiting on it would answer about a state nobody asked for."
638
657
  )
639
658
  % [node_path, property, type_string(type), str(given), GdhValues.cut_by(type)]
640
659
  )
@@ -651,9 +670,11 @@ static func _cut_to_fit(given: Variant, type: int, node_path: String, property:
651
670
  ## where a number goes became 0.0, 0.0 already equalled the property, and the wait answered met the
652
671
  ## instant it started, about a state nobody asked about, with the real value beside it. A refusal
653
672
  ## is read and a met is acted on.
654
- static func _not_comparable(current: Variant, wanted: Variant, node_path: String, property: String) -> String:
673
+ static func _not_comparable(
674
+ current: Variant, wanted: Variant, node_path: String, property: String
675
+ ) -> String:
655
676
  var types: Array[String] = [
656
- node_path, property, type_string(typeof(current)), type_string(typeof(wanted))
677
+ node_path, property, type_string(typeof(current)), type_string(typeof(wanted)),
657
678
  ]
658
679
  # A value that cannot become what the property holds is said as that, which names the remedy,
659
680
  # before the comparison it would also fail; an object on either side is said as the comparison.
@@ -696,7 +717,9 @@ static func _not_comparable(current: Variant, wanted: Variant, node_path: String
696
717
  ## when the property is no enum or the value no word.
697
718
  static func _enum_by_name(watched: Dictionary, sent: Variant, node_path: String) -> Array:
698
719
  var declaration: Dictionary = watched["declaration"]
699
- var enum_hint: bool = GdhRead.as_int(declaration.get("hint", PROPERTY_HINT_NONE)) == PROPERTY_HINT_ENUM
720
+ var enum_hint: bool = (
721
+ GdhRead.as_int(declaration.get("hint", PROPERTY_HINT_NONE)) == PROPERTY_HINT_ENUM
722
+ )
700
723
  if watched["declared"] != TYPE_INT or not enum_hint or not sent is String:
701
724
  return []
702
725
  if not watched["holder"] is Object:
@@ -721,7 +744,7 @@ static func _watched(node: Node, node_path: String, property: String) -> Diction
721
744
  var named: String = reached["name"]
722
745
  var missing: String = GdhPaths.nothing_under(holder, named, str(reached["called"]))
723
746
  if not missing.is_empty():
724
- return {"type": "error", "message": missing}
747
+ return { "type": "error", "message": missing }
725
748
  var slot: Dictionary = _declared_slot(holder, named)
726
749
  return {
727
750
  "value": GdhPaths.read_under(holder, named),
@@ -731,14 +754,15 @@ static func _watched(node: Node, node_path: String, property: String) -> Diction
731
754
  }
732
755
 
733
756
 
734
- ## What [param named] is declared with on [param holder], as [method GdhValues.slot_declared] answers,
735
- ## read the way a set reads it, so the two refuse the same values: a property's declaration, or a
736
- ## typed list's or map's element type. Read off properties alone, an element of an Array[Node] holding
737
- ## null declares nothing, and a wait for 5 on it runs out the timeout while a set of 5 on it is refused
738
- ## at once. TYPE_NIL for anything declaring none, and for a call, whose answer nothing declares.
757
+ ## What [param named] is declared with on [param holder], as [method GdhValues.slot_declared]
758
+ ## answers, read the way a set reads it, so the two refuse the same values: a property's
759
+ ## declaration, or a typed list's or map's element type. Read off properties alone, an element of an
760
+ ## Array[Node] holding null declares nothing, and a wait for 5 on it runs out the timeout while a
761
+ ## set of 5 on it is refused at once. TYPE_NIL for anything declaring none, and for a call, whose
762
+ ## answer nothing declares.
739
763
  static func _declared_slot(holder: Variant, named: String) -> Dictionary:
740
764
  if not GdhPaths.method_of(named).is_empty():
741
- return {"type": TYPE_NIL, "class": "", "script": null, "declared": {}}
765
+ return { "type": TYPE_NIL, "class": "", "script": null, "declared": {} }
742
766
  return GdhValues.slot_declared(holder, named)
743
767
 
744
768
 
@@ -756,15 +780,17 @@ static func _declared_slot(holder: Variant, named: String) -> Dictionary:
756
780
  ## screen fell back to counting frames. [param include_hidden] asks the other question, for a
757
781
  ## caller waiting on words a hidden node holds.
758
782
  ##
759
- ## Paced by what a look costs, so the watching leaves the game its speed: every frame while a look is
760
- ## cheap, and on a screen large enough for one to cost a real share of a frame, often enough that
783
+ ## Paced by what a look costs, so the watching leaves the game its speed: every frame while a look
784
+ ## is cheap, and on a screen large enough for one to cost a real share of a frame, often enough that
761
785
  ## looking takes about a tenth of the time. Every frame on a hall of 3,500 nodes held the game at a
762
786
  ## sixth of its frame rate, and its clock, which advances by the frame's delta, lost three quarters
763
787
  ## of its time, so a wait for a date timed out on a clock that turned a day every thirty seconds.
764
788
  ## The answer says how many frames the wait spanned, how many looks it took and how long they took
765
789
  ## between them, which is how a caller sees what it cost and whether the game lost its speed to the
766
790
  ## looks or to something else.
767
- func _wait_until_said(node_path: String, said: String, timeout_ms: int, include_hidden: bool) -> Dictionary:
791
+ func _wait_until_said(
792
+ node_path: String, said: String, timeout_ms: int, include_hidden: bool
793
+ ) -> Dictionary:
768
794
  var started: int = Time.get_ticks_msec()
769
795
  var frames: int = 0
770
796
  var looks: int = 0
@@ -814,8 +840,9 @@ func _wait_until_said(node_path: String, said: String, timeout_ms: int, include_
814
840
  if not found and not include_hidden and _anything_says(node_path, said, true):
815
841
  answer["unseen_note"] = (
816
842
  (
817
- "Something under %s says it, but is hidden or drawn outside the viewport or a"
818
- + " ScrollContainer, where the player cannot read it; includeHidden true counts it there too"
843
+ "Something under %s says it, but is hidden or drawn outside the viewport or a "
844
+ + "ScrollContainer, where the player cannot read it; includeHidden true counts it "
845
+ + "there too"
819
846
  )
820
847
  % node_path
821
848
  )
@@ -900,8 +927,8 @@ class GdhTrace:
900
927
  elif value is Dictionary:
901
928
  var map: Dictionary = value
902
929
  _last = map.duplicate(true)
903
- # Game time unrounded: a frame can be a tenth of a millisecond long, and a rate worked out over
904
- # one from times cut to a ten-thousandth was off by its own size.
930
+ # Game time unrounded: a frame can be a tenth of a millisecond long, and a rate worked out
931
+ # over one from times cut to a ten-thousandth was off by its own size.
905
932
  var change: Dictionary = {
906
933
  "frame": frame,
907
934
  "elapsed_ms": elapsed,
@@ -1,5 +1,4 @@
1
1
  extends RefCounted
2
-
3
2
  ## What a node says to a player, read the way it is drawn: the parsed words of rich text, as much of
4
3
  ## a line as is typed out so far, the translation rather than its key, a mask over a secret, and the
5
4
  ## items of the controls that draw a list.
@@ -21,9 +20,9 @@ extends RefCounted
21
20
  ## for each of them on every look a wait takes.
22
21
  static func said_by(node: Node) -> String:
23
22
  var own: String = drawn_by(node)
24
- # A control showing a picture rather than words is named for a player who cannot see it, and that
25
- # name is the one the words a caller asks by are meant for: the rail's "Out" tab is a square with
26
- # an icon on it, and a click on "Out" found only the words around it.
23
+ # A control showing a picture rather than words is named for a player who cannot see it, and
24
+ # that name is the one the words a caller asks by are meant for: the rail's "Out" tab is a
25
+ # square with an icon on it, and a click on "Out" found only the words around it.
27
26
  if own.is_empty() and node is Control and not node is Label and not draws_a_list(node):
28
27
  var control: Control = node
29
28
  return control.atr(control.accessibility_name).strip_edges()
@@ -141,7 +140,9 @@ static func masked(field: LineEdit) -> String:
141
140
  ## Whether [param node] draws a list of items rather than holding words in a `text`; see
142
141
  ## [method items_of].
143
142
  static func draws_a_list(node: Node) -> bool:
144
- return node is TabBar or node is MenuBar or node is ItemList or node is PopupMenu or node is Tree
143
+ return (
144
+ node is TabBar or node is MenuBar or node is ItemList or node is PopupMenu or node is Tree
145
+ )
145
146
 
146
147
 
147
148
  ## The words of the enabled buttons under [param from], quoted, each once and in the order they are
@@ -154,7 +155,9 @@ static func pressable_words(from: Node, open: Callable, most: int) -> String:
154
155
  while not pending.is_empty():
155
156
  var node: Node = pending.pop_front()
156
157
  var button: BaseButton = node as BaseButton
157
- var words: String = "" if button == null or button.disabled else said_by(button).strip_edges()
158
+ var words: String = (
159
+ "" if button == null or button.disabled else said_by(button).strip_edges()
160
+ )
158
161
  var quoted: String = '"%s"' % words
159
162
  if not words.is_empty() and not offered.has(quoted) and open.call(button):
160
163
  if offered.size() == most:
@@ -175,7 +178,7 @@ static func _keep(line: String, into: Array[String]) -> void:
175
178
  static func _keep_item(line: String, item: Variant, into: Array[Dictionary]) -> void:
176
179
  var trimmed: String = line.strip_edges()
177
180
  if not trimmed.is_empty():
178
- into.append({"says": trimmed, "item": item})
181
+ into.append({ "says": trimmed, "item": item })
179
182
 
180
183
 
181
184
  ## [param words] as [param node] draws them, which for a game with translations is the translation