gdharness 1.3.2 → 1.3.4
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 +163 -85
- package/build/godot/addons/gdharness_editor/animation_reads.gd +14 -11
- package/build/godot/addons/gdharness_editor/declared_values.gd +157 -4
- package/build/godot/addons/gdharness_editor/file_reads.gd +15 -3
- package/build/godot/addons/gdharness_editor/file_walk.gd +134 -0
- package/build/godot/addons/gdharness_editor/file_walk.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/headless_check.gd +44 -23
- package/build/godot/addons/gdharness_editor/property_paths.gd +225 -0
- package/build/godot/addons/gdharness_editor/property_paths.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +249 -227
- package/build/godot/addons/gdharness_editor/reading.gd +15 -0
- package/build/godot/addons/gdharness_editor/same_values.gd +177 -0
- package/build/godot/addons/gdharness_editor/same_values.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +100 -15
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -3
- package/build/godot/addons/gdharness_editor/tool_executor.gd +12 -3
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +402 -55
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +3 -2
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +91 -0
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +88 -5
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +102 -337
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +363 -0
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +15 -0
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +10 -26
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +3 -2
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +2 -1
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +30 -0
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -3
- package/build/godot/operations/animation_reads.gd +14 -11
- package/build/godot/operations/declared_values.gd +157 -4
- package/build/godot/operations/dependencies.gd +14 -39
- package/build/godot/operations/editor_tools.gd +7 -3
- package/build/godot/operations/file_reads.gd +15 -3
- package/build/godot/operations/file_walk.gd +1 -0
- package/build/godot/operations/gdscript_analysis.gd +229 -75
- package/build/godot/operations/gdscript_authoring.gd +142 -34
- package/build/godot/operations/import_pipeline.gd +17 -6
- package/build/godot/operations/logger.gd +7 -0
- package/build/godot/operations/property_paths.gd +225 -0
- package/build/godot/operations/property_values.gd +249 -227
- package/build/godot/operations/reading.gd +15 -0
- package/build/godot/operations/same_values.gd +177 -0
- package/build/godot/operations/serialisation.gd +75 -3
- package/package.json +1 -1
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
extends RefCounted
|
|
3
|
+
|
|
4
|
+
## Where a property a tool writes lives: on the object given, on a resource built into it, or on an
|
|
5
|
+
## element of one of its lists that the write has to make first.
|
|
6
|
+
|
|
7
|
+
const GdhRead = preload("reading.gd")
|
|
8
|
+
|
|
9
|
+
const HEADINGS: int = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## The entry [param target] declares for [param property], or {} when it declares none, headings
|
|
13
|
+
## left out.
|
|
14
|
+
static func declared_on(target: Object, property: String) -> Dictionary:
|
|
15
|
+
for declared: Dictionary in target.get_property_list():
|
|
16
|
+
if str(declared.get("name", "")) != property:
|
|
17
|
+
continue
|
|
18
|
+
if GdhRead.as_int(declared.get("usage", 0)) & HEADINGS:
|
|
19
|
+
continue
|
|
20
|
+
return declared
|
|
21
|
+
return {}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Where [param key] is written: as [holder, name, through], the object holding the property, its
|
|
25
|
+
## name there, and the resources passed on the way. A key that is none of [param target]'s own may
|
|
26
|
+
## start with the name of one holding a resource built into it, then a slash and a key of that
|
|
27
|
+
## resource: "sources/0/0:0/0/probability" is the probability of tile 0:0 in the atlas source a
|
|
28
|
+
## TileSet keeps as sources/0. A colon path cannot reach it, since the tile's own name holds colons.
|
|
29
|
+
## A resource kept in a file of its own is not reached, nor, given the file [param home] that
|
|
30
|
+
## [param target] is saved in, one built into another file, since writing it here would leave that
|
|
31
|
+
## file as it was.
|
|
32
|
+
static func holder_of(target: Object, key: String, home: String = "") -> Array:
|
|
33
|
+
var through: Array[Resource] = []
|
|
34
|
+
var holder: Object = target
|
|
35
|
+
var name: String = key
|
|
36
|
+
while declared_on(holder, name).is_empty():
|
|
37
|
+
var reached: Resource = null
|
|
38
|
+
var at: int = name.find("/")
|
|
39
|
+
while at > 0:
|
|
40
|
+
var held: Resource = built_in_at(holder, name.left(at), home)
|
|
41
|
+
if held != null:
|
|
42
|
+
reached = held
|
|
43
|
+
break
|
|
44
|
+
at = name.find("/", at + 1)
|
|
45
|
+
if reached == null:
|
|
46
|
+
break
|
|
47
|
+
through.append(reached)
|
|
48
|
+
holder = reached
|
|
49
|
+
name = name.substr(at + 1)
|
|
50
|
+
return [holder, name, through]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## The resource [param target] holds under [param property] when it is built into a file, or null;
|
|
54
|
+
## given [param home], only one built into that file or into none yet.
|
|
55
|
+
static func built_in_at(target: Object, property: String, home: String = "") -> Resource:
|
|
56
|
+
var held: Resource = held_at(target, property)
|
|
57
|
+
if held == null or not held.is_built_in():
|
|
58
|
+
return null
|
|
59
|
+
var file: String = held.resource_path.get_slice("::", 0)
|
|
60
|
+
return held if home.is_empty() or file.is_empty() or file == home else null
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## The file [param target] is saved in: its scene's for a node, as the nodes of an instance are
|
|
64
|
+
## saved in the scene holding it, and its own for a resource; "" when it has none yet.
|
|
65
|
+
static func home_of(target: Object) -> String:
|
|
66
|
+
if target is Node:
|
|
67
|
+
var top: Node = target
|
|
68
|
+
while top.owner != null:
|
|
69
|
+
top = top.owner
|
|
70
|
+
return top.scene_file_path
|
|
71
|
+
if target is Resource:
|
|
72
|
+
var resource: Resource = target
|
|
73
|
+
return resource.resource_path.get_slice("::", 0)
|
|
74
|
+
return ""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## The resource [param target] holds under [param property], built in or kept in a file, or null.
|
|
78
|
+
static func held_at(target: Object, property: String) -> Resource:
|
|
79
|
+
if declared_on(target, property).is_empty():
|
|
80
|
+
return null
|
|
81
|
+
var held: Variant = target.get(property)
|
|
82
|
+
return held if held is Resource else null
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## Makes the element of a list [param name] names on [param target] when it is the next one, as the
|
|
86
|
+
## inspector's Add Element does, and answers "" (also when [param name] is in no list), or why not,
|
|
87
|
+
## [param called] being what [param target] is called. The engine lists an element's properties
|
|
88
|
+
## only once the element exists, so a TileSet with no physics layer had no
|
|
89
|
+
## physics_layer_0/collision_layer to write. A list the engine declares as an array grows through
|
|
90
|
+
## the method named after its elements, add_physics_layer for physics_layer_, given the indices of
|
|
91
|
+
## the lists around it; a list counted by a property, as a tile's polygons are by polygons_count,
|
|
92
|
+
## grows by raising the count.
|
|
93
|
+
static func grow(target: Object, name: String, called: String) -> String:
|
|
94
|
+
var steps: PackedStringArray = name.split("/")
|
|
95
|
+
for depth: int in steps.size() - 1:
|
|
96
|
+
var step: String = steps[depth]
|
|
97
|
+
var cut: int = step.rfind("_")
|
|
98
|
+
if cut < 1 or not step.substr(cut + 1).is_valid_int():
|
|
99
|
+
continue
|
|
100
|
+
var index: int = step.substr(cut + 1).to_int()
|
|
101
|
+
var above: String = "/".join(steps.slice(0, depth))
|
|
102
|
+
var under: String = above + "/" if not above.is_empty() else ""
|
|
103
|
+
var prefix: String = under + step.left(cut + 1)
|
|
104
|
+
var element: String = step.left(cut)
|
|
105
|
+
var counted: String = under + element + "s_count"
|
|
106
|
+
var count: int = -1
|
|
107
|
+
var adding: Callable
|
|
108
|
+
if not declared_on(target, counted).is_empty():
|
|
109
|
+
count = GdhRead.as_int(target.get(counted))
|
|
110
|
+
adding = func() -> void: target.set(counted, count + 1)
|
|
111
|
+
elif _listed_array(target, prefix):
|
|
112
|
+
count = _elements(target, prefix)
|
|
113
|
+
var method: String = "add_" + element
|
|
114
|
+
var outer: Array = []
|
|
115
|
+
for enclosing: String in steps.slice(0, depth):
|
|
116
|
+
var at: int = enclosing.rfind("_")
|
|
117
|
+
if at > 0 and enclosing.substr(at + 1).is_valid_int():
|
|
118
|
+
outer.append(enclosing.substr(at + 1).to_int())
|
|
119
|
+
if target.has_method(method):
|
|
120
|
+
adding = func() -> void: target.callv(method, outer)
|
|
121
|
+
if count < 0 or index < count:
|
|
122
|
+
continue
|
|
123
|
+
var held: String = (
|
|
124
|
+
"%s has %d %s%s"
|
|
125
|
+
% [
|
|
126
|
+
above if not above.is_empty() else called,
|
|
127
|
+
count,
|
|
128
|
+
element.replace("_", " "),
|
|
129
|
+
"" if count == 1 else "s"
|
|
130
|
+
]
|
|
131
|
+
)
|
|
132
|
+
if index > count:
|
|
133
|
+
return (
|
|
134
|
+
"%s, and a new one goes after the last, so %s%d cannot be written yet: write %s%d first."
|
|
135
|
+
% [held, prefix, index, prefix, count]
|
|
136
|
+
)
|
|
137
|
+
if not adding.is_valid():
|
|
138
|
+
return "%s, and nothing makes another, so %s cannot be written." % [held, name]
|
|
139
|
+
adding.call()
|
|
140
|
+
return ""
|
|
141
|
+
return ""
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
## Whether [param target] declares a list whose elements' properties start with [param prefix],
|
|
145
|
+
## as TileSet declares physics_layers for physics_layer_0/collision_layer.
|
|
146
|
+
static func _listed_array(target: Object, prefix: String) -> bool:
|
|
147
|
+
for declared: Dictionary in target.get_property_list():
|
|
148
|
+
var usage: int = GdhRead.as_int(declared.get("usage", 0))
|
|
149
|
+
if usage & PROPERTY_USAGE_ARRAY and str(declared.get("class_name", "")) == prefix:
|
|
150
|
+
return true
|
|
151
|
+
return false
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## How many elements of the list whose properties start with [param prefix] [param target] holds.
|
|
155
|
+
static func _elements(target: Object, prefix: String) -> int:
|
|
156
|
+
var count: int = 0
|
|
157
|
+
for declared: Dictionary in target.get_property_list():
|
|
158
|
+
var name: String = str(declared.get("name", ""))
|
|
159
|
+
if not name.begins_with(prefix):
|
|
160
|
+
continue
|
|
161
|
+
var index: String = name.substr(prefix.length()).get_slice("/", 0)
|
|
162
|
+
if index.is_valid_int():
|
|
163
|
+
count = maxi(count, index.to_int() + 1)
|
|
164
|
+
return count
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
## What [param target] is called in a refusal: the class_name its script declares, or else its
|
|
168
|
+
## engine class. A resource made as a project class is a Resource to the engine, and "Resource.kind"
|
|
169
|
+
## named a property no Resource has.
|
|
170
|
+
static func class_called(target: Object) -> String:
|
|
171
|
+
var attached: Variant = target.get_script()
|
|
172
|
+
if attached is Script:
|
|
173
|
+
var script: Script = attached
|
|
174
|
+
if not str(script.get_global_name()).is_empty():
|
|
175
|
+
return str(script.get_global_name())
|
|
176
|
+
return target.get_class()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## The refusal for [param name], which [param target] does not declare, when it runs through a
|
|
180
|
+
## resource saved in a file other than [param home], the one [param target] is saved in; or "". The
|
|
181
|
+
## write belongs in that file, since this one would be saved and that one left as it was: through a
|
|
182
|
+
## TileMapLayer's tile_set kept in a file of its own, or through the mesh of an instance's editable
|
|
183
|
+
## child, which is built into the scene the instance comes from.
|
|
184
|
+
static func held_elsewhere(target: Object, name: String, home: String) -> String:
|
|
185
|
+
var at: int = name.find("/")
|
|
186
|
+
while at > 0:
|
|
187
|
+
var head: String = name.left(at)
|
|
188
|
+
var rest: String = name.substr(at + 1)
|
|
189
|
+
var held: Resource = held_at(target, head)
|
|
190
|
+
var file: String = "" if held == null else held.resource_path.get_slice("::", 0)
|
|
191
|
+
if held != null and not held.is_built_in():
|
|
192
|
+
return (
|
|
193
|
+
"%s.%s is %s, a file of its own, so %s is written there, with resource_edit modify on %s."
|
|
194
|
+
% [class_called(target), head, file, rest, file]
|
|
195
|
+
)
|
|
196
|
+
if held != null and not file.is_empty() and not home.is_empty() and file != home:
|
|
197
|
+
return _built_in_elsewhere(target, head, rest, held, file)
|
|
198
|
+
at = name.find("/", at + 1)
|
|
199
|
+
return ""
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
## The refusal for writing [param rest] into [param held], which [param target] holds under
|
|
203
|
+
## [param head] and which is built into [param file], the scene an instance in this one comes from.
|
|
204
|
+
static func _built_in_elsewhere(
|
|
205
|
+
target: Object, head: String, rest: String, held: Resource, file: String
|
|
206
|
+
) -> String:
|
|
207
|
+
var node: String = ""
|
|
208
|
+
if target is Node:
|
|
209
|
+
var placed: Node = target
|
|
210
|
+
var from: Node = placed
|
|
211
|
+
while from != null and from.scene_file_path != file:
|
|
212
|
+
from = from.owner
|
|
213
|
+
if from != null:
|
|
214
|
+
node = " on %s" % from.get_path_to(placed)
|
|
215
|
+
var text: String = (
|
|
216
|
+
(
|
|
217
|
+
"%s.%s is built into %s, so %s is written there, with scene_node set%s in %s, which changes "
|
|
218
|
+
+ "it wherever that scene is used. To change it here alone, give %s a %s of its own: a "
|
|
219
|
+
+ 'value with "_type" set to its class and its properties beside it.'
|
|
220
|
+
)
|
|
221
|
+
% [class_called(target), head, file, rest, node, file, head, held.get_class()]
|
|
222
|
+
)
|
|
223
|
+
if target is GeometryInstance3D and (held is Material or rest.contains("material")):
|
|
224
|
+
text += " A material for this instance alone goes in material_override."
|
|
225
|
+
return text
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://mi7vnabydtlw
|