gdharness 1.3.8 → 1.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +4668 -3229
- package/build/godot/addons/gdharness_editor/animation_reads.gd +115 -4
- package/build/godot/addons/gdharness_editor/declared_values.gd +272 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +115 -18
- package/build/godot/addons/gdharness_editor/headless_check.gd +49 -2
- package/build/godot/addons/gdharness_editor/property_paths.gd +89 -16
- package/build/godot/addons/gdharness_editor/property_values.gd +180 -121
- package/build/godot/addons/gdharness_editor/reading.gd +112 -6
- package/build/godot/addons/gdharness_editor/scene_file.gd +117 -174
- package/build/godot/addons/gdharness_editor/scene_uids.gd +107 -0
- package/build/godot/addons/gdharness_editor/scene_uids.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/serialisation.gd +19 -29
- package/build/godot/addons/gdharness_editor/setting_reads.gd +121 -0
- package/build/godot/addons/gdharness_editor/setting_reads.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tool_executor.gd +39 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +263 -0
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +49 -414
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +464 -0
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +37 -0
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +221 -0
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +162 -483
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -0
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +8 -4
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +2 -1
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +10 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +151 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +7 -30
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +190 -38
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +485 -0
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd +128 -1
- package/build/godot/addons/gdharness_runtime/call_errors.gd +8 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +272 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +112 -6
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +35 -3
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +5 -0
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +188 -30
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +107 -104
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +17 -0
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +13 -4
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +320 -27
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +165 -20
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +49 -3
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +31 -2
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +250 -10
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +279 -11
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +27 -0
- package/build/godot/addons/gdharness_runtime/serialisation.gd +19 -29
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +128 -1
- package/build/godot/operations/animation_reads.gd +115 -4
- package/build/godot/operations/audio_buses.gd +93 -7
- package/build/godot/operations/classdb_queries.gd +119 -7
- package/build/godot/operations/declared_values.gd +272 -0
- package/build/godot/operations/dependencies.gd +154 -41
- package/build/godot/operations/editor_tools.gd +29 -13
- package/build/godot/operations/export_presets.gd +602 -4
- package/build/godot/operations/file_reads.gd +115 -18
- package/build/godot/operations/gdscript_analysis.gd +14 -0
- package/build/godot/operations/gdscript_authoring.gd +12 -2
- package/build/godot/operations/godot_operations.gd +18 -2
- package/build/godot/operations/import_pipeline.gd +20 -221
- package/build/godot/operations/input_actions.gd +127 -51
- package/build/godot/operations/project_config.gd +113 -68
- package/build/godot/operations/project_diagnostics.gd +4 -0
- package/build/godot/operations/project_validation.gd +266 -0
- package/build/godot/operations/property_paths.gd +89 -16
- package/build/godot/operations/property_values.gd +180 -121
- package/build/godot/operations/reading.gd +112 -6
- package/build/godot/operations/scene_connections.gd +222 -0
- package/build/godot/operations/serialisation.gd +19 -29
- package/build/godot/operations/setting_reads.gd +121 -0
- package/build/godot/operations/value_shapes.gd +128 -1
- package/build/godot/tests/listed.gd +20 -13
- package/build/keeper.js +1402 -707
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ extends RefCounted
|
|
|
8
8
|
## only true of a running game: reaching a node by path, and fitting an argument to the type the
|
|
9
9
|
## method it is going to declares.
|
|
10
10
|
|
|
11
|
+
const GdhDeclared = preload("declared_values.gd")
|
|
11
12
|
const GdhRead = preload("reading.gd")
|
|
12
13
|
const GdhSerialisation = preload("serialisation.gd")
|
|
13
14
|
const GdhShapes = preload("value_shapes.gd")
|
|
@@ -31,6 +32,9 @@ const SINGLETONS: Array[String] = [
|
|
|
31
32
|
## The most frames a press is held in one call, as many as a frames wait takes.
|
|
32
33
|
const MOST_HELD: int = 600
|
|
33
34
|
|
|
35
|
+
## Read by [method _key_names], once a game.
|
|
36
|
+
static var _named_keys: PackedStringArray = []
|
|
37
|
+
|
|
34
38
|
var _shared: GdhSerialisation = GdhSerialisation.new()
|
|
35
39
|
|
|
36
40
|
|
|
@@ -77,9 +81,18 @@ static func node_at(root: Node, path: String) -> Dictionary:
|
|
|
77
81
|
|
|
78
82
|
## "Node not found" for [param path], with the deepest node of it the game has and the children that
|
|
79
83
|
## node has instead, as a scene's refusal names them: a mistyped step was otherwise found by reading
|
|
80
|
-
## the whole tree.
|
|
84
|
+
## the whole tree. A path written from the current scene rather than from the root, as a scene's
|
|
85
|
+
## tools take one, is given the path that reaches it.
|
|
81
86
|
static func _not_found(root: Node, path: String) -> String:
|
|
82
87
|
var said: String = "Node not found: " + path
|
|
88
|
+
var scene: Node = root.get_tree().current_scene if root.is_inside_tree() else null
|
|
89
|
+
if not path.begins_with("/") and scene != null:
|
|
90
|
+
var meant: Node = scene.get_node_or_null(path)
|
|
91
|
+
if meant != null:
|
|
92
|
+
return (
|
|
93
|
+
"%s. Paths here go from the root, /root, not from the current scene, so this one is %s."
|
|
94
|
+
% [said, str(meant.get_path())]
|
|
95
|
+
)
|
|
83
96
|
var parts: PackedStringArray = path.split("/", false)
|
|
84
97
|
var at: Node = root
|
|
85
98
|
var step: int = 0
|
|
@@ -160,12 +173,7 @@ static func glyph_of(keycode: Key, shifted: bool) -> int:
|
|
|
160
173
|
## engine's own names, printable keys and the special ones above [constant KEY_SPECIAL], since the
|
|
161
174
|
## game takes any name OS.find_keycode_from_string does.
|
|
162
175
|
static func unknown_key(name: String) -> String:
|
|
163
|
-
var
|
|
164
|
-
for code: int in range(32, 127) + range(KEY_SPECIAL + 1, KEY_SPECIAL + 0x100):
|
|
165
|
-
var spelled: String = OS.get_keycode_string(code as Key)
|
|
166
|
-
if not spelled.is_empty() and OS.find_keycode_from_string(spelled) == code:
|
|
167
|
-
var _named: bool = names.append(spelled)
|
|
168
|
-
var close: PackedStringArray = GdhRead.nearest(name, names)
|
|
176
|
+
var close: PackedStringArray = GdhRead.nearest(name, _key_names())
|
|
169
177
|
var said: String = "No key is called %s." % name
|
|
170
178
|
if close.size() == 1:
|
|
171
179
|
said += " The nearest key name is %s." % close[0]
|
|
@@ -178,6 +186,151 @@ static func unknown_key(name: String) -> String:
|
|
|
178
186
|
)
|
|
179
187
|
|
|
180
188
|
|
|
189
|
+
## Every key name the engine takes, read once a game. The engine spells a code it has no name for as
|
|
190
|
+
## the character it would be, and past the last code point that is an error it prints: 136 of them
|
|
191
|
+
## for the special range, two seconds of console on a fast machine, more than a call's whole wait on
|
|
192
|
+
## a loaded one, and each in the game's log as an error the game never made. So they are asked with
|
|
193
|
+
## the engine's error printing off, which no other line of the main thread runs beside.
|
|
194
|
+
static func _key_names() -> PackedStringArray:
|
|
195
|
+
if not _named_keys.is_empty():
|
|
196
|
+
return _named_keys
|
|
197
|
+
var printing: bool = Engine.print_error_messages
|
|
198
|
+
Engine.print_error_messages = false
|
|
199
|
+
for code: int in range(32, 127) + range(KEY_SPECIAL + 1, KEY_SPECIAL + 0x100):
|
|
200
|
+
var spelled: String = OS.get_keycode_string(code as Key)
|
|
201
|
+
if not spelled.is_empty() and OS.find_keycode_from_string(spelled) == code:
|
|
202
|
+
var _named: bool = _named_keys.append(spelled)
|
|
203
|
+
Engine.print_error_messages = printing
|
|
204
|
+
return _named_keys
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
## The refusal for [param action], which the input map lacks, with the nearest of the project's own
|
|
208
|
+
## actions, or else all of them. The engine's ui_ ones are in every project, and offered for any name
|
|
209
|
+
## they only buried the project's ("dance" is near ui_cancel), so they are offered for a ui_ name only.
|
|
210
|
+
static func no_action(action: String) -> String:
|
|
211
|
+
var engine: PackedStringArray = []
|
|
212
|
+
var own: PackedStringArray = []
|
|
213
|
+
for named: StringName in InputMap.get_actions():
|
|
214
|
+
if str(named).begins_with("ui_"):
|
|
215
|
+
var _listed: bool = engine.append(str(named))
|
|
216
|
+
else:
|
|
217
|
+
var _owned: bool = own.append(str(named))
|
|
218
|
+
own.sort()
|
|
219
|
+
var said: String = "Action not found: %s" % action
|
|
220
|
+
var close: PackedStringArray = GdhRead.nearest(action, engine if action.begins_with("ui_") else own)
|
|
221
|
+
if not close.is_empty():
|
|
222
|
+
return "%s; the nearest %s %s." % [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
223
|
+
if own.is_empty():
|
|
224
|
+
return "%s; the project defines no actions of its own, and the engine's are the ui_ ones." % said
|
|
225
|
+
return "%s; the project's own actions are %s, beside the engine's ui_ ones." % [said, ", ".join(own)]
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
## The actions of the input map that [param event] stands for, as Input reads it for
|
|
229
|
+
## is_action_pressed: a key or a button bound to an action presses that action too, so a game
|
|
230
|
+
## counting the action counted the key, and the answer had said only that a key went in.
|
|
231
|
+
static func actions_of(event: InputEvent) -> Array[String]:
|
|
232
|
+
var named: Array[String] = []
|
|
233
|
+
# A motion matches every action on its axis, either way and at any distance, which would name
|
|
234
|
+
# ui_left for a stick held right; Input presses only those its direction reaches past their
|
|
235
|
+
# deadzone.
|
|
236
|
+
var moved: bool = event is InputEventJoypadMotion
|
|
237
|
+
for action: StringName in InputMap.get_actions():
|
|
238
|
+
if event.is_action_pressed(action) if moved else InputMap.event_is_action(event, action):
|
|
239
|
+
named.append(str(action))
|
|
240
|
+
named.sort()
|
|
241
|
+
return named
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
## The path of the Control holding the keyboard focus in [param host]'s viewport, or "" when none
|
|
245
|
+
## does. Read before an event goes in, since pressing a focused button can move the focus.
|
|
246
|
+
static func focus_path(host: Node) -> String:
|
|
247
|
+
var holding: Control = host.get_viewport().gui_get_focus_owner()
|
|
248
|
+
return "" if holding == null else str(holding.get_path())
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
## [param said] with [param focused], the Control that had the focus, under focused. A key, an action
|
|
252
|
+
## or a pad's event reaches that Control before the game's _unhandled_input: Space on a focused Button
|
|
253
|
+
## pressed it and the game's own handler counted the key as well, and a stick pushed down moved a
|
|
254
|
+
## focused list's selection and never reached the game, with nothing in the answer saying why.
|
|
255
|
+
##
|
|
256
|
+
## Where the focus is once the event has been handled goes under focused_after when it moved, null
|
|
257
|
+
## when nothing holds it then: ui_down on a focused button hands the focus to its neighbour, and an
|
|
258
|
+
## answer naming only the button that took the press read as the focus never having moved.
|
|
259
|
+
static func focus_noted(said: Dictionary, focused: String, host: Node) -> Dictionary:
|
|
260
|
+
if not focused.is_empty():
|
|
261
|
+
said["focused"] = focused
|
|
262
|
+
var now: String = focus_path(host)
|
|
263
|
+
if now.is_empty() and not focused.is_empty():
|
|
264
|
+
said["focused_after"] = null
|
|
265
|
+
elif now != focused:
|
|
266
|
+
said["focused_after"] = now
|
|
267
|
+
return said
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
## Presses the buttons under [param root] whose Shortcut [param action] matches, as the player's key
|
|
271
|
+
## bound to it would, and answers their paths under "shortcuts" and the one that took the press
|
|
272
|
+
## under "shortcut_pressed". A Viewport hands a shortcut only keys, joypad buttons and
|
|
273
|
+
## InputEventShortcut, so an action pressed on its own never reached a Button whose Shortcut holds
|
|
274
|
+
## that action, which is how a game gives a button a binding the player can change.
|
|
275
|
+
##
|
|
276
|
+
## A key goes to the shortcuts last in the tree first and stops at the first button that takes it,
|
|
277
|
+
## while an InputEventShortcut reaches only the buttons holding that one Shortcut. So each Shortcut
|
|
278
|
+
## is sent in that order, a frame apart, until a button is pressed; a button still decides by its
|
|
279
|
+
## disabled, its visibility and its shortcut context, as it does for the key.
|
|
280
|
+
static func press_shortcuts(root: Node, action: String) -> Dictionary:
|
|
281
|
+
var pressing: InputEventAction = InputEventAction.new()
|
|
282
|
+
pressing.action = action
|
|
283
|
+
pressing.pressed = true
|
|
284
|
+
var buttons: Array[BaseButton] = []
|
|
285
|
+
for node: Node in root.find_children("*", "BaseButton", true, false):
|
|
286
|
+
var button: BaseButton = node
|
|
287
|
+
if button.shortcut != null and button.shortcut.matches_event(pressing):
|
|
288
|
+
buttons.append(button)
|
|
289
|
+
var paths: Array[String] = []
|
|
290
|
+
for button: BaseButton in buttons:
|
|
291
|
+
paths.append(str(button.get_path()))
|
|
292
|
+
buttons.reverse()
|
|
293
|
+
var taken: Array[String] = []
|
|
294
|
+
var heard: Array[Callable] = []
|
|
295
|
+
for button: BaseButton in buttons:
|
|
296
|
+
var path: String = str(button.get_path())
|
|
297
|
+
var hearing: Callable = func() -> void: taken.append(path)
|
|
298
|
+
heard.append(hearing)
|
|
299
|
+
var _listening: int = button.pressed.connect(hearing)
|
|
300
|
+
var sent: Dictionary = {}
|
|
301
|
+
for button: BaseButton in buttons:
|
|
302
|
+
if not taken.is_empty():
|
|
303
|
+
break
|
|
304
|
+
if sent.has(button.shortcut):
|
|
305
|
+
continue
|
|
306
|
+
sent[button.shortcut] = true
|
|
307
|
+
var through: InputEventShortcut = InputEventShortcut.new()
|
|
308
|
+
through.shortcut = button.shortcut
|
|
309
|
+
Input.parse_input_event(through)
|
|
310
|
+
await root.get_tree().process_frame
|
|
311
|
+
for at: int in buttons.size():
|
|
312
|
+
if is_instance_valid(buttons[at]):
|
|
313
|
+
buttons[at].pressed.disconnect(heard[at])
|
|
314
|
+
var pressed: Variant = null
|
|
315
|
+
if not taken.is_empty():
|
|
316
|
+
pressed = taken[0]
|
|
317
|
+
return {"shortcuts": paths, "shortcut_pressed": pressed}
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
## The methods [param object] answers to, as the engine lists them: for a script itself, the static
|
|
321
|
+
## functions it declares before the methods every script has. The engine's list for a script holds
|
|
322
|
+
## only those, so a static function called on its class was read as taking nothing.
|
|
323
|
+
static func methods_of(object: Object) -> Array[Dictionary]:
|
|
324
|
+
var listed: Array[Dictionary] = []
|
|
325
|
+
if object is Script:
|
|
326
|
+
var script: Script = object
|
|
327
|
+
for entry: Dictionary in script.get_script_method_list():
|
|
328
|
+
if GdhRead.as_int(entry.get("flags", 0)) & METHOD_FLAG_STATIC:
|
|
329
|
+
listed.append(entry)
|
|
330
|
+
listed.append_array(object.get_method_list())
|
|
331
|
+
return listed
|
|
332
|
+
|
|
333
|
+
|
|
181
334
|
## What [param path] names: a node under [param root], as [method node_at] reads it, or an engine
|
|
182
335
|
## singleton by its name. Answers the object under "object", and under "node" the node that objects
|
|
183
336
|
## named in a value are found from, which for a singleton is [param root].
|
|
@@ -203,6 +356,32 @@ static func target_at(root: Node, path: String) -> Dictionary:
|
|
|
203
356
|
return {"object": standing["node"], "node": standing["node"]}
|
|
204
357
|
|
|
205
358
|
|
|
359
|
+
## The script [param path] names for a call of one of its static functions, as [method target_at]
|
|
360
|
+
## answers a node, with [param root] as the node objects named in its arguments are found from: a
|
|
361
|
+
## script by its res:// path, or the global class of that name where no node is at [param path].
|
|
362
|
+
## Answers {} when it names a node or no class, and a message for a res:// path holding no script.
|
|
363
|
+
## A class no node carries could not be called at all, and each way of naming it read as a node or
|
|
364
|
+
## a method misspelt.
|
|
365
|
+
static func class_at(root: Node, path: String) -> Dictionary:
|
|
366
|
+
var script_path: String = path
|
|
367
|
+
if not path.begins_with("res://"):
|
|
368
|
+
if not path.is_valid_ascii_identifier() or not node_at(root, path).has("message"):
|
|
369
|
+
return {}
|
|
370
|
+
script_path = ""
|
|
371
|
+
for declared: Dictionary in ProjectSettings.get_global_class_list():
|
|
372
|
+
if str(declared.get("class", "")) == path:
|
|
373
|
+
script_path = str(declared.get("path", ""))
|
|
374
|
+
if script_path.is_empty():
|
|
375
|
+
return {}
|
|
376
|
+
var loaded: Resource = load(script_path) if ResourceLoader.exists(script_path) else null
|
|
377
|
+
if not loaded is Script:
|
|
378
|
+
return {
|
|
379
|
+
"type": "error",
|
|
380
|
+
"message": "No script is at %s to call a static function of." % script_path,
|
|
381
|
+
}
|
|
382
|
+
return {"object": loaded, "node": root}
|
|
383
|
+
|
|
384
|
+
|
|
206
385
|
## Whether two values can be compared without the comparison itself failing.
|
|
207
386
|
static func comparable(one: Variant, other: Variant) -> bool:
|
|
208
387
|
return GdhSerialisation.comparable(one, other)
|
|
@@ -253,7 +432,7 @@ static func remedy(value: Variant, type: int) -> String:
|
|
|
253
432
|
## The declared type of one parameter of a method, or TYPE_NIL when the method or the parameter
|
|
254
433
|
## is not there, which reads as "no opinion".
|
|
255
434
|
func parameter_type(object: Object, method: String, index: int) -> int:
|
|
256
|
-
for entry: Dictionary in object
|
|
435
|
+
for entry: Dictionary in methods_of(object):
|
|
257
436
|
if entry.get("name", "") != method:
|
|
258
437
|
continue
|
|
259
438
|
var params: Array = entry.get("args", [])
|
|
@@ -264,10 +443,59 @@ func parameter_type(object: Object, method: String, index: int) -> int:
|
|
|
264
443
|
return TYPE_NIL
|
|
265
444
|
|
|
266
445
|
|
|
446
|
+
## The values of the enum argument [param index] of [param method] declares, by name, as
|
|
447
|
+
## `{"enum": "Mood", "values": {...}}`, or {} when it declares none. The engine gives such an argument
|
|
448
|
+
## the type int and the enum's name, as res://main.gd.Mood or Node.ProcessMode.
|
|
449
|
+
func parameter_enum(object: Object, method: String, index: int) -> Dictionary:
|
|
450
|
+
for entry: Dictionary in methods_of(object):
|
|
451
|
+
if entry.get("name", "") != method:
|
|
452
|
+
continue
|
|
453
|
+
var params: Array = entry.get("args", [])
|
|
454
|
+
if index < 0 or index >= params.size():
|
|
455
|
+
return {}
|
|
456
|
+
var parameter: Dictionary = params[index]
|
|
457
|
+
if GdhRead.as_int(parameter.get("usage", 0)) & PROPERTY_USAGE_CLASS_IS_ENUM == 0:
|
|
458
|
+
return {}
|
|
459
|
+
var qualified: String = str(parameter.get("class_name", ""))
|
|
460
|
+
var enum_name: String = qualified.get_slice(".", qualified.get_slice_count(".") - 1)
|
|
461
|
+
var values: Dictionary = GdhDeclared.enum_declared_as(qualified)
|
|
462
|
+
if values.is_empty():
|
|
463
|
+
values = _enum_along(object if object is Script else object.get_script(), enum_name)
|
|
464
|
+
if values.is_empty():
|
|
465
|
+
return {}
|
|
466
|
+
return {"enum": enum_name, "values": values}
|
|
467
|
+
return {}
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
## The enum [param named] declared by [param script] or a script it extends, or {}. A script built in
|
|
471
|
+
## a scene or made at run time names its enums by an id nothing loads, gdscript://-92233….gd.Mood
|
|
472
|
+
## measured on 4.7.2, and a method of the object called declares its enums along this chain.
|
|
473
|
+
static func _enum_along(script: Variant, named: String) -> Dictionary:
|
|
474
|
+
if not script is Script:
|
|
475
|
+
return {}
|
|
476
|
+
var current: Script = script
|
|
477
|
+
while current != null:
|
|
478
|
+
var found: Variant = current.get_script_constant_map().get(named)
|
|
479
|
+
if found is Dictionary:
|
|
480
|
+
var values: Dictionary = found
|
|
481
|
+
return values
|
|
482
|
+
current = current.get_base_script()
|
|
483
|
+
return {}
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
## An empty list typed as what the packed array [param type] holds, for its elements to be fitted
|
|
487
|
+
## against one by one, or null for any other type.
|
|
488
|
+
static func packed_template(type: int) -> Variant:
|
|
489
|
+
if not GdhSerialisation.LIST_TYPES.has(type):
|
|
490
|
+
return null
|
|
491
|
+
var element: int = GdhShapes.PACKED_ELEMENTS[type]
|
|
492
|
+
return Array([], element, &"", null)
|
|
493
|
+
|
|
494
|
+
|
|
267
495
|
## The class a method declares for argument [param index], or "" when it names none: an untyped
|
|
268
496
|
## parameter, a Variant, or a type that is not an object.
|
|
269
497
|
func parameter_class(object: Object, method: String, index: int) -> String:
|
|
270
|
-
for entry: Dictionary in object
|
|
498
|
+
for entry: Dictionary in methods_of(object):
|
|
271
499
|
if entry.get("name", "") != method:
|
|
272
500
|
continue
|
|
273
501
|
var params: Array = entry.get("args", [])
|
|
@@ -413,7 +641,7 @@ func fitted(value: Variant, type: int) -> Variant:
|
|
|
413
641
|
## An empty list or map typed the way argument [param index] of [param method] declares, or null
|
|
414
642
|
## when that argument is not a typed container. What [method typed_like] builds against.
|
|
415
643
|
func parameter_container(object: Object, method: String, index: int) -> Variant:
|
|
416
|
-
for entry: Dictionary in object
|
|
644
|
+
for entry: Dictionary in methods_of(object):
|
|
417
645
|
if entry.get("name", "") != method:
|
|
418
646
|
continue
|
|
419
647
|
var params: Array = entry.get("args", [])
|
|
@@ -555,6 +783,18 @@ func _element(item: Variant, builtin: int, declared: String, script: Script, res
|
|
|
555
783
|
if named.has("message"):
|
|
556
784
|
return {"message": str(named["message"]) + "."}
|
|
557
785
|
return {"value": named["object"]}
|
|
786
|
+
# Fitted alone, 4.5 into an Array[int] went in as 4, as a single value is refused for.
|
|
787
|
+
if cuts(item, builtin):
|
|
788
|
+
return {
|
|
789
|
+
"message":
|
|
790
|
+
(
|
|
791
|
+
"is %s, which %s cannot hold without changing it."
|
|
792
|
+
% [JSON.stringify(serialize(item), "", false), cut_by(builtin)]
|
|
793
|
+
)
|
|
794
|
+
}
|
|
795
|
+
var rounded: String = inexact(item, builtin)
|
|
796
|
+
if not rounded.is_empty():
|
|
797
|
+
return {"message": "is too large to be sure of: %s." % rounded}
|
|
558
798
|
var fitted_item: Variant = fitted(item, builtin)
|
|
559
799
|
if not acceptable(fitted_item, builtin):
|
|
560
800
|
# The value as it was sent rather than its engine type: JSON numbers arrive as floats, so a 7
|