gdharness 1.2.28 → 1.2.30
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 +395 -169
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +259 -31
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +168 -98
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +33 -7
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -3
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +89 -15
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +142 -0
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +1 -13
- package/build/index.js +384 -169
- package/build/keeper.js +20 -17
- package/package.json +1 -1
|
@@ -11,6 +11,25 @@ extends RefCounted
|
|
|
11
11
|
const Read = preload("reading.gd")
|
|
12
12
|
const Serialisation = preload("serialisation.gd")
|
|
13
13
|
|
|
14
|
+
## The engine singletons a call, a write or a read may name instead of a node: what the engine
|
|
15
|
+
## measures, and the engine settings that are the game's own. Not every singleton: OS runs programs
|
|
16
|
+
## and opens files, DisplayServer switches the keyboard's layout and Input moves the real pointer,
|
|
17
|
+
## and none of that is a game's to do to the machine it runs on.
|
|
18
|
+
const SINGLETONS: Array[String] = [
|
|
19
|
+
"Engine",
|
|
20
|
+
"Performance",
|
|
21
|
+
"RenderingServer",
|
|
22
|
+
"AudioServer",
|
|
23
|
+
"PhysicsServer2D",
|
|
24
|
+
"PhysicsServer3D",
|
|
25
|
+
"NavigationServer2D",
|
|
26
|
+
"NavigationServer3D",
|
|
27
|
+
"Time",
|
|
28
|
+
"TranslationServer",
|
|
29
|
+
]
|
|
30
|
+
## The most frames a press is held in one call, as many as a frames wait takes.
|
|
31
|
+
const MOST_HELD: int = 600
|
|
32
|
+
|
|
14
33
|
var _shared: Serialisation = Serialisation.new()
|
|
15
34
|
|
|
16
35
|
|
|
@@ -55,6 +74,56 @@ static func node_at(root: Node, path: String) -> Dictionary:
|
|
|
55
74
|
return {"node": node}
|
|
56
75
|
|
|
57
76
|
|
|
77
|
+
## How many process frames a whole press holds before its release, from `holdFrames`: one unless
|
|
78
|
+
## asked, as the halves of a press have always been a frame apart. Held inside the game because the
|
|
79
|
+
## time between a press, a wait and a release sent as three calls counts as held too: a 60-frame
|
|
80
|
+
## pour poured for nearly three seconds. A String saying why, where the hold cannot be.
|
|
81
|
+
static func held_for(params: Dictionary) -> Variant:
|
|
82
|
+
if not params.has("holdFrames"):
|
|
83
|
+
return 1
|
|
84
|
+
if params.has("pressed"):
|
|
85
|
+
return (
|
|
86
|
+
"holdFrames holds a whole press and then lets go, and pressed asks for one half of one:"
|
|
87
|
+
+ " give one or the other."
|
|
88
|
+
)
|
|
89
|
+
var frames: int = Read.as_int(params["holdFrames"], 0)
|
|
90
|
+
if frames < 1 or frames > MOST_HELD:
|
|
91
|
+
return "holdFrames is %d, and it takes 1 to %d." % [frames, MOST_HELD]
|
|
92
|
+
return frames
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## What a press says it was held for: [param frames] for a whole one, nothing for one half.
|
|
96
|
+
static func held_answer(whole: bool, frames: int) -> Variant:
|
|
97
|
+
if whole:
|
|
98
|
+
return frames
|
|
99
|
+
return null
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## What [param path] names: a node under [param root], as [method node_at] reads it, or an engine
|
|
103
|
+
## singleton by its name. Answers the object under "object", and under "node" the node that objects
|
|
104
|
+
## named in a value are found from, which for a singleton is [param root].
|
|
105
|
+
static func target_at(root: Node, path: String) -> Dictionary:
|
|
106
|
+
if SINGLETONS.has(path):
|
|
107
|
+
return {"object": Engine.get_singleton(path), "node": root}
|
|
108
|
+
if not path.contains("/") and Engine.has_singleton(path):
|
|
109
|
+
return {
|
|
110
|
+
"type": "error",
|
|
111
|
+
"message":
|
|
112
|
+
(
|
|
113
|
+
(
|
|
114
|
+
"%s is an engine singleton, and the ones that can be reached are %s. The rest act"
|
|
115
|
+
+ " on the machine rather than the game: OS runs programs, DisplayServer switches the"
|
|
116
|
+
+ " keyboard's layout and Input moves the real pointer."
|
|
117
|
+
)
|
|
118
|
+
% [path, ", ".join(SINGLETONS)]
|
|
119
|
+
),
|
|
120
|
+
}
|
|
121
|
+
var standing: Dictionary = node_at(root, path)
|
|
122
|
+
if standing.has("message"):
|
|
123
|
+
return standing
|
|
124
|
+
return {"object": standing["node"], "node": standing["node"]}
|
|
125
|
+
|
|
126
|
+
|
|
58
127
|
## Whether two values can be compared without the comparison itself failing.
|
|
59
128
|
static func comparable(one: Variant, other: Variant) -> bool:
|
|
60
129
|
return Serialisation.comparable(one, other)
|
|
@@ -65,6 +134,28 @@ static func acceptable(value: Variant, type: int) -> bool:
|
|
|
65
134
|
return Serialisation.acceptable(value, type)
|
|
66
135
|
|
|
67
136
|
|
|
137
|
+
## Whether [param given] would have to be cut to become a [param type]: a fraction made a whole
|
|
138
|
+
## number, or a number other than 0 or 1 made a truth.
|
|
139
|
+
##
|
|
140
|
+
## A number arrives from JSON as a float, so one is converted to what the slot holds, and the
|
|
141
|
+
## conversion truncates: 2.5 written into an int was stored as 2 and answered as written, and 0.2 on
|
|
142
|
+
## a bool became true. A number written as text counts as the number.
|
|
143
|
+
static func cuts(given: Variant, type: int) -> bool:
|
|
144
|
+
if type != TYPE_INT and type != TYPE_BOOL:
|
|
145
|
+
return false
|
|
146
|
+
var number: Variant = given
|
|
147
|
+
if given is String:
|
|
148
|
+
var text: String = given
|
|
149
|
+
number = Read.json_or_null(text)
|
|
150
|
+
if typeof(number) != TYPE_FLOAT and typeof(number) != TYPE_INT:
|
|
151
|
+
return false
|
|
152
|
+
var value: float = Read.as_float(number)
|
|
153
|
+
var whole: bool = value == floorf(value)
|
|
154
|
+
if type == TYPE_INT:
|
|
155
|
+
return not whole
|
|
156
|
+
return not (whole and (value == 0.0 or value == 1.0))
|
|
157
|
+
|
|
158
|
+
|
|
68
159
|
## How a value of [param type] is written to be read as one, or empty when it is not built from fields.
|
|
69
160
|
static func written_as(type: int) -> String:
|
|
70
161
|
return Serialisation.written_as(type)
|
|
@@ -98,6 +189,52 @@ func parameter_class(object: Object, method: String, index: int) -> String:
|
|
|
98
189
|
return ""
|
|
99
190
|
|
|
100
191
|
|
|
192
|
+
## The components a path reads off [param value], by the names GDScript gives them, or none for a
|
|
193
|
+
## value that has none: a vector's axes, a colour's channels, a rectangle's corners and size, a
|
|
194
|
+
## transform's basis and origin. A value of one of these is a copy wherever it is read, so a write
|
|
195
|
+
## into a component is a write into the copy until it is put back where it was read from.
|
|
196
|
+
static func components_of(value: Variant) -> PackedStringArray:
|
|
197
|
+
match typeof(value):
|
|
198
|
+
TYPE_VECTOR2, TYPE_VECTOR2I:
|
|
199
|
+
return PackedStringArray(["x", "y"])
|
|
200
|
+
TYPE_VECTOR3, TYPE_VECTOR3I:
|
|
201
|
+
return PackedStringArray(["x", "y", "z"])
|
|
202
|
+
TYPE_VECTOR4, TYPE_VECTOR4I, TYPE_QUATERNION:
|
|
203
|
+
return PackedStringArray(["x", "y", "z", "w"])
|
|
204
|
+
TYPE_COLOR:
|
|
205
|
+
return PackedStringArray(
|
|
206
|
+
[
|
|
207
|
+
"r",
|
|
208
|
+
"g",
|
|
209
|
+
"b",
|
|
210
|
+
"a",
|
|
211
|
+
"r8",
|
|
212
|
+
"g8",
|
|
213
|
+
"b8",
|
|
214
|
+
"a8",
|
|
215
|
+
"h",
|
|
216
|
+
"s",
|
|
217
|
+
"v",
|
|
218
|
+
"ok_hsl_h",
|
|
219
|
+
"ok_hsl_s",
|
|
220
|
+
"ok_hsl_l"
|
|
221
|
+
]
|
|
222
|
+
)
|
|
223
|
+
TYPE_RECT2, TYPE_RECT2I, TYPE_AABB:
|
|
224
|
+
return PackedStringArray(["position", "size", "end"])
|
|
225
|
+
TYPE_PLANE:
|
|
226
|
+
return PackedStringArray(["normal", "x", "y", "z", "d"])
|
|
227
|
+
TYPE_BASIS:
|
|
228
|
+
return PackedStringArray(["x", "y", "z"])
|
|
229
|
+
TYPE_TRANSFORM2D:
|
|
230
|
+
return PackedStringArray(["x", "y", "origin"])
|
|
231
|
+
TYPE_TRANSFORM3D:
|
|
232
|
+
return PackedStringArray(["basis", "origin"])
|
|
233
|
+
TYPE_PROJECTION:
|
|
234
|
+
return PackedStringArray(["x", "y", "z", "w"])
|
|
235
|
+
return PackedStringArray()
|
|
236
|
+
|
|
237
|
+
|
|
101
238
|
## What the slot [param named] on [param holder] declares it holds, as `{"type": int, "class":
|
|
102
239
|
## String, "script": Script or null}`: a property's declaration, or the element type of a typed list
|
|
103
240
|
## or the value type of a typed map. TYPE_NIL when nothing is declared, which is also what a
|
|
@@ -111,6 +248,11 @@ func parameter_class(object: Object, method: String, index: int) -> String:
|
|
|
111
248
|
## as a script class with no `class_name` is declared under the engine class the script extends,
|
|
112
249
|
## measured on 4.7.2: `var quarrel: Walker` with Walker a preloaded Node3D script lists as Node3D.
|
|
113
250
|
static func slot_declared(holder: Variant, named: String) -> Dictionary:
|
|
251
|
+
# A component holds one kind of value and no other: a vector's axis is a number, its integer
|
|
252
|
+
# form's an integer, a rectangle's size a vector of the same form.
|
|
253
|
+
if named in components_of(holder):
|
|
254
|
+
var component: Variant = holder[named]
|
|
255
|
+
return {"type": typeof(component), "class": "", "script": null}
|
|
114
256
|
if holder is Array:
|
|
115
257
|
var items: Array = holder
|
|
116
258
|
if items.is_typed():
|
|
@@ -312,19 +312,7 @@ static func _in_order(compare: String, limit: float, current: Variant) -> bool:
|
|
|
312
312
|
## conversion truncates: 3.5 waited on an int became 3 and 0.2 on a bool became true, and a property
|
|
313
313
|
## already holding that answered met at once, about a state nobody asked for.
|
|
314
314
|
static func _cut_to_fit(given: Variant, type: int, node_path: String, property: String) -> String:
|
|
315
|
-
if
|
|
316
|
-
return ""
|
|
317
|
-
var number: Variant = given
|
|
318
|
-
if given is String:
|
|
319
|
-
var text: String = given
|
|
320
|
-
number = Read.json_or_null(text)
|
|
321
|
-
if typeof(number) != TYPE_FLOAT and typeof(number) != TYPE_INT:
|
|
322
|
-
return ""
|
|
323
|
-
var value: float = Read.as_float(number)
|
|
324
|
-
var whole: bool = value == floorf(value)
|
|
325
|
-
if type == TYPE_INT and whole:
|
|
326
|
-
return ""
|
|
327
|
-
if type == TYPE_BOOL and whole and (value == 0.0 or value == 1.0):
|
|
315
|
+
if not Values.cuts(given, type):
|
|
328
316
|
return ""
|
|
329
317
|
return (
|
|
330
318
|
(
|