gdharness 1.3.9 → 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.
Files changed (73) hide show
  1. package/build/cli.js +4563 -3195
  2. package/build/godot/addons/gdharness_editor/animation_reads.gd +115 -4
  3. package/build/godot/addons/gdharness_editor/declared_values.gd +272 -0
  4. package/build/godot/addons/gdharness_editor/file_reads.gd +102 -18
  5. package/build/godot/addons/gdharness_editor/headless_check.gd +49 -2
  6. package/build/godot/addons/gdharness_editor/property_paths.gd +89 -16
  7. package/build/godot/addons/gdharness_editor/property_values.gd +180 -121
  8. package/build/godot/addons/gdharness_editor/reading.gd +51 -1
  9. package/build/godot/addons/gdharness_editor/scene_file.gd +77 -140
  10. package/build/godot/addons/gdharness_editor/scene_uids.gd +107 -0
  11. package/build/godot/addons/gdharness_editor/scene_uids.gd.uid +1 -0
  12. package/build/godot/addons/gdharness_editor/serialisation.gd +11 -22
  13. package/build/godot/addons/gdharness_editor/setting_reads.gd +121 -0
  14. package/build/godot/addons/gdharness_editor/setting_reads.gd.uid +1 -0
  15. package/build/godot/addons/gdharness_editor/tool_executor.gd +28 -5
  16. package/build/godot/addons/gdharness_editor/tools/animation_states.gd +39 -20
  17. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +49 -362
  18. package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +464 -0
  19. package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd.uid +1 -0
  20. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +37 -0
  21. package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +221 -0
  22. package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd.uid +1 -0
  23. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +147 -492
  24. package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -0
  25. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +4 -3
  26. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +2 -1
  27. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +79 -5
  28. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +7 -30
  29. package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +183 -37
  30. package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +485 -0
  31. package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd.uid +1 -0
  32. package/build/godot/addons/gdharness_editor/value_shapes.gd +128 -1
  33. package/build/godot/addons/gdharness_runtime/declared_values.gd +272 -0
  34. package/build/godot/addons/gdharness_runtime/reading.gd +51 -1
  35. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +35 -3
  36. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +185 -30
  37. package/build/godot/addons/gdharness_runtime/runtime_input.gd +86 -79
  38. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +17 -0
  39. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +13 -4
  40. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +309 -22
  41. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +163 -20
  42. package/build/godot/addons/gdharness_runtime/runtime_screen.gd +49 -3
  43. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +5 -2
  44. package/build/godot/addons/gdharness_runtime/runtime_values.gd +146 -5
  45. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +279 -11
  46. package/build/godot/addons/gdharness_runtime/runtime_words.gd +27 -0
  47. package/build/godot/addons/gdharness_runtime/serialisation.gd +11 -22
  48. package/build/godot/addons/gdharness_runtime/value_shapes.gd +128 -1
  49. package/build/godot/operations/animation_reads.gd +115 -4
  50. package/build/godot/operations/audio_buses.gd +93 -7
  51. package/build/godot/operations/classdb_queries.gd +119 -7
  52. package/build/godot/operations/declared_values.gd +272 -0
  53. package/build/godot/operations/dependencies.gd +154 -41
  54. package/build/godot/operations/editor_tools.gd +22 -14
  55. package/build/godot/operations/export_presets.gd +602 -4
  56. package/build/godot/operations/file_reads.gd +102 -18
  57. package/build/godot/operations/gdscript_authoring.gd +11 -1
  58. package/build/godot/operations/godot_operations.gd +16 -2
  59. package/build/godot/operations/import_pipeline.gd +20 -221
  60. package/build/godot/operations/input_actions.gd +108 -47
  61. package/build/godot/operations/project_config.gd +83 -69
  62. package/build/godot/operations/project_diagnostics.gd +4 -0
  63. package/build/godot/operations/project_validation.gd +266 -0
  64. package/build/godot/operations/property_paths.gd +89 -16
  65. package/build/godot/operations/property_values.gd +180 -121
  66. package/build/godot/operations/reading.gd +51 -1
  67. package/build/godot/operations/scene_connections.gd +222 -0
  68. package/build/godot/operations/serialisation.gd +11 -22
  69. package/build/godot/operations/setting_reads.gd +121 -0
  70. package/build/godot/operations/value_shapes.gd +128 -1
  71. package/build/godot/tests/listed.gd +20 -13
  72. package/build/keeper.js +1371 -706
  73. package/package.json +1 -1
@@ -0,0 +1,221 @@
1
+ @tool
2
+ extends RefCounted
3
+
4
+ ## Bakes what a NavigationRegion2D or NavigationRegion3D in a scene file navigates by, as the
5
+ ## editor's Bake button does, and saves it where the region keeps it.
6
+ ##
7
+ ## The navigation servers read source geometry only off nodes inside the scene tree, which a scene
8
+ ## opened for a tool is not: measured on 4.7.2, parsing outside it fails with "The root node needs
9
+ ## to be inside the SceneTree." What goes into the tree is a copy made without scripts, so a
10
+ ## headless engine runs none of the game's code for it, and taken out again in the same call. Its
11
+ ## source group is renamed to one only the copy uses, since the editor's tree also holds the scene
12
+ ## being edited, whose nodes would otherwise be read with it.
13
+
14
+ const GdhSceneFile = preload("../scene_file.gd")
15
+ const GdhSceneUids = preload("../scene_uids.gd")
16
+
17
+ ## The names NavigationPolygon.SourceGeometryMode and NavigationMesh.SourceGeometryMode give each
18
+ ## mode, which share their values.
19
+ const SOURCE_MODES: Array[String] = ["root_node_children", "groups_with_children", "groups_explicit"]
20
+
21
+
22
+ ## Bakes the region [param region] of [param scene], answering what it now holds or why not.
23
+ static func bake(scene: GdhSceneFile, region: Node) -> Dictionary:
24
+ var at: String = scene.path_of(region)
25
+ var flat: bool = region is NavigationRegion2D
26
+ if not flat and not region is NavigationRegion3D:
27
+ return scene.refuse(
28
+ "%s is a %s, not a NavigationRegion2D or a NavigationRegion3D." % [at, region.get_class()]
29
+ )
30
+ var property: String = "navigation_polygon" if flat else "navigation_mesh"
31
+ var held: Resource = region.get(property)
32
+ var made: bool = held == null
33
+ if made:
34
+ held = NavigationPolygon.new()
35
+ if not flat:
36
+ held = NavigationMesh.new()
37
+ var file: String = held.resource_path.get_slice("::", 0)
38
+ var embedded: bool = file.is_empty() or held.resource_path.contains("::")
39
+ if embedded and not file.is_empty() and file != scene.path:
40
+ return scene.refuse(
41
+ (
42
+ (
43
+ "The %s %s holds is built into %s, and a bake here cannot save that file. Bake the "
44
+ + "region there, or save the %s to a file of its own."
45
+ )
46
+ % [held.get_class(), at, file, held.get_class()]
47
+ )
48
+ )
49
+
50
+ scene.asked(region, [property])
51
+ if made:
52
+ region.set(property, held)
53
+ var parsed: bool = _parse_and_bake(scene, at, held, flat)
54
+ var polygons: int = _polygon_count(held)
55
+ var answer: Dictionary = {
56
+ "ok": true,
57
+ "nodePath": at,
58
+ "polygons": polygons,
59
+ "vertices": _vertex_count(held),
60
+ }
61
+ if made:
62
+ answer["made"] = (
63
+ "%s held no %s, so one was made and built into the scene, with the defaults." % [at, property]
64
+ )
65
+ if polygons == 0:
66
+ answer["note"] = _why_empty(held, flat, parsed)
67
+ if not flat and not Engine.is_editor_hint() and _reads_meshes(region, held):
68
+ answer["meshesNote"] = (
69
+ "The engine's warning that it had to parse RenderingServer meshes at runtime is about a "
70
+ + "game rebaking as it runs, where reading a mesh back from the GPU stalls a frame. A "
71
+ + "headless engine gives it for any bake read from MeshInstance3D nodes; this one ran once "
72
+ + "and is saved."
73
+ )
74
+
75
+ if not embedded:
76
+ scene.close()
77
+ var saving: Error = GdhSceneUids.save_keeping_uid(held, file)
78
+ if saving != OK:
79
+ return {"ok": false, "error": "Failed to save %s: %s" % [file, error_string(saving)]}
80
+ if Engine.is_editor_hint():
81
+ EditorInterface.get_resource_filesystem().update_file(file)
82
+ answer["savedTo"] = file
83
+ return answer
84
+
85
+ var written: Dictionary = scene.write(
86
+ func(saved: Node) -> String:
87
+ var kept: Resource = saved.get_node(at).get(property)
88
+ var loaded: int = _polygon_count(kept) if kept != null else -1
89
+ if loaded != polygons:
90
+ return "%s would load holding %d polygons, not %d." % [at, loaded, polygons]
91
+ return ""
92
+ )
93
+ if not written.is_empty():
94
+ return written
95
+ answer["savedTo"] = scene.path
96
+ return answer
97
+
98
+
99
+ ## Reads the source geometry for [param held] off a copy of the region at [param at] and bakes it
100
+ ## into [param held], answering whether the copy gave any geometry.
101
+ static func _parse_and_bake(scene: GdhSceneFile, at: String, held: Resource, flat: bool) -> bool:
102
+ var copy: Node = scene.root.duplicate(Node.DUPLICATE_GROUPS)
103
+ var reading: Resource = held.duplicate()
104
+ var group_property: String = "source_geometry_group_name" if flat else "geometry_source_group_name"
105
+ var group: String = str(held.get(group_property))
106
+ var own_group: String = "_gdh_bake_%d" % copy.get_instance_id()
107
+ reading.set(group_property, own_group)
108
+ for node: Node in _all_under(copy):
109
+ if node.is_in_group(group):
110
+ node.add_to_group(own_group)
111
+ var tree: SceneTree = Engine.get_main_loop() as SceneTree
112
+ tree.root.add_child(copy)
113
+ var region: Node = copy.get_node(at)
114
+ var found: bool = false
115
+ if flat:
116
+ var outline: NavigationPolygon = reading
117
+ var geometry: NavigationMeshSourceGeometryData2D = NavigationMeshSourceGeometryData2D.new()
118
+ NavigationServer2D.parse_source_geometry_data(outline, geometry, region)
119
+ found = geometry.has_data()
120
+ var polygon: NavigationPolygon = held
121
+ NavigationServer2D.bake_from_source_geometry_data(polygon, geometry)
122
+ else:
123
+ var shape: NavigationMesh = reading
124
+ var geometry: NavigationMeshSourceGeometryData3D = NavigationMeshSourceGeometryData3D.new()
125
+ NavigationServer3D.parse_source_geometry_data(shape, geometry, region)
126
+ found = geometry.has_data()
127
+ var mesh: NavigationMesh = held
128
+ NavigationServer3D.bake_from_source_geometry_data(mesh, geometry)
129
+ tree.root.remove_child(copy)
130
+ copy.free()
131
+ return found
132
+
133
+
134
+ ## Whether a bake of [param held] for [param region] reads the mesh of any node, which only a
135
+ ## NavigationMesh reading mesh instances does.
136
+ static func _reads_meshes(region: Node, held: Resource) -> bool:
137
+ var mesh: NavigationMesh = held
138
+ if mesh.geometry_parsed_geometry_type == NavigationMesh.PARSED_GEOMETRY_STATIC_COLLIDERS:
139
+ return false
140
+ var read: Array[Node] = []
141
+ match mesh.geometry_source_geometry_mode:
142
+ NavigationMesh.SOURCE_GEOMETRY_ROOT_NODE_CHILDREN:
143
+ read = _all_under(region)
144
+ NavigationMesh.SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN:
145
+ for node: Node in _all_under(region.owner if region.owner != null else region):
146
+ if node.is_in_group(mesh.geometry_source_group_name):
147
+ read.append_array(_all_under(node))
148
+ _:
149
+ for node: Node in _all_under(region.owner if region.owner != null else region):
150
+ if node.is_in_group(mesh.geometry_source_group_name):
151
+ read.append(node)
152
+ return read.any(
153
+ func(node: Node) -> bool:
154
+ return node is MeshInstance3D or node is MultiMeshInstance3D or node is GridMap
155
+ )
156
+
157
+
158
+ static func _all_under(top: Node) -> Array[Node]:
159
+ var nodes: Array[Node] = [top]
160
+ for child: Node in top.get_children():
161
+ nodes.append_array(_all_under(child))
162
+ return nodes
163
+
164
+
165
+ static func _polygon_count(held: Resource) -> int:
166
+ if held is NavigationPolygon:
167
+ var polygon: NavigationPolygon = held
168
+ return polygon.get_polygon_count()
169
+ var mesh: NavigationMesh = held
170
+ return mesh.get_polygon_count()
171
+
172
+
173
+ static func _vertex_count(held: Resource) -> int:
174
+ if held is NavigationPolygon:
175
+ var polygon: NavigationPolygon = held
176
+ return polygon.vertices.size()
177
+ var mesh: NavigationMesh = held
178
+ return mesh.vertices.size()
179
+
180
+
181
+ ## Why a bake of [param held] gave no polygons, from whether any source geometry was read.
182
+ static func _why_empty(held: Resource, flat: bool, parsed: bool) -> String:
183
+ if flat:
184
+ var polygon: NavigationPolygon = held
185
+ if polygon.get_outline_count() == 0:
186
+ return (
187
+ "The NavigationPolygon has no outlines, and a 2D bake fills only the area its "
188
+ + "outlines enclose, less what the obstacles inside take. Give it outlines with "
189
+ + "scene_node set, such as navigation_polygon/outlines [[[0, 0], [512, 0], [512, 512], "
190
+ + "[0, 512]]], then bake again."
191
+ )
192
+ return (
193
+ (
194
+ "Nothing was left to walk inside the outlines once the obstacles read from %s, and the "
195
+ % _source_said(polygon.source_geometry_mode, polygon.source_geometry_group_name)
196
+ )
197
+ + "agent_radius %s kept from their edges, were taken out." % polygon.agent_radius
198
+ )
199
+ var mesh: NavigationMesh = held
200
+ if not parsed:
201
+ return (
202
+ (
203
+ "No geometry was read from %s with geometry_parsed_geometry_type %s, so there was nothing to "
204
+ % [
205
+ _source_said(mesh.geometry_source_geometry_mode, mesh.geometry_source_group_name),
206
+ ["mesh_instances", "static_colliders", "both"][mesh.geometry_parsed_geometry_type],
207
+ ]
208
+ )
209
+ + "bake: mesh_instances reads MeshInstance3D nodes, static_colliders the shapes of physics "
210
+ + "bodies on geometry_collision_mask, and both reads both."
211
+ )
212
+ return (
213
+ "The geometry read gave no floor an agent of agent_height %s and agent_radius %s can stand on."
214
+ % [mesh.agent_height, mesh.agent_radius]
215
+ )
216
+
217
+
218
+ static func _source_said(mode: int, group: String) -> String:
219
+ if mode == 0:
220
+ return "the region's children (source mode root_node_children)"
221
+ return "the nodes in the group %s (source mode %s)" % [group, SOURCE_MODES[mode]]
@@ -0,0 +1 @@
1
+ uid://cm0m0jpgoyqwr