@satelliteoflove/godot-mcp 2.3.0 → 2.4.0
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/README.md +3 -4
- package/addon/command_router.gd +39 -0
- package/addon/command_router.gd.uid +1 -0
- package/addon/commands/animation_commands.gd +633 -0
- package/addon/commands/animation_commands.gd.uid +1 -0
- package/addon/commands/debug_commands.gd +109 -0
- package/addon/commands/debug_commands.gd.uid +1 -0
- package/addon/commands/file_commands.gd +95 -0
- package/addon/commands/file_commands.gd.uid +1 -0
- package/addon/commands/node_commands.gd +252 -0
- package/addon/commands/node_commands.gd.uid +1 -0
- package/addon/commands/project_commands.gd +114 -0
- package/addon/commands/project_commands.gd.uid +1 -0
- package/addon/commands/resource_commands.gd +293 -0
- package/addon/commands/resource_commands.gd.uid +1 -0
- package/addon/commands/scene3d_commands.gd +169 -0
- package/addon/commands/scene3d_commands.gd.uid +1 -0
- package/addon/commands/scene_commands.gd +131 -0
- package/addon/commands/scene_commands.gd.uid +1 -0
- package/addon/commands/screenshot_commands.gd +130 -0
- package/addon/commands/screenshot_commands.gd.uid +1 -0
- package/addon/commands/script_commands.gd +156 -0
- package/addon/commands/script_commands.gd.uid +1 -0
- package/addon/commands/selection_commands.gd +170 -0
- package/addon/commands/selection_commands.gd.uid +1 -0
- package/addon/commands/system_commands.gd +29 -0
- package/addon/commands/tilemap_commands.gd +657 -0
- package/addon/commands/tilemap_commands.gd.uid +1 -0
- package/addon/core/base_command.gd +29 -0
- package/addon/core/base_command.gd.uid +1 -0
- package/addon/core/mcp_debugger_plugin.gd +144 -0
- package/addon/core/mcp_debugger_plugin.gd.uid +1 -0
- package/addon/core/mcp_logger.gd +40 -0
- package/addon/core/mcp_logger.gd.uid +1 -0
- package/addon/core/mcp_utils.gd +125 -0
- package/addon/core/mcp_utils.gd.uid +1 -0
- package/addon/game_bridge/mcp_game_bridge.gd +195 -0
- package/addon/game_bridge/mcp_game_bridge.gd.uid +1 -0
- package/addon/plugin.cfg +8 -0
- package/addon/plugin.gd +89 -0
- package/addon/plugin.gd.uid +1 -0
- package/addon/ui/status_panel.gd +23 -0
- package/addon/ui/status_panel.gd.uid +1 -0
- package/addon/ui/status_panel.tscn +41 -0
- package/addon/websocket_server.gd +143 -0
- package/addon/websocket_server.gd.uid +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +60 -0
- package/dist/cli.js.map +1 -0
- package/dist/connection/websocket.d.ts +14 -0
- package/dist/connection/websocket.d.ts.map +1 -1
- package/dist/connection/websocket.js +69 -1
- package/dist/connection/websocket.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/installer/install.d.ts +13 -0
- package/dist/installer/install.d.ts.map +1 -0
- package/dist/installer/install.js +77 -0
- package/dist/installer/install.js.map +1 -0
- package/dist/tools/project.d.ts +3 -3
- package/dist/tools/project.d.ts.map +1 -1
- package/dist/tools/project.js +28 -2
- package/dist/tools/project.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +20 -0
- package/dist/version.js.map +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
extends MCPBaseCommand
|
|
3
|
+
class_name MCPTilemapCommands
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
func get_commands() -> Dictionary:
|
|
7
|
+
return {
|
|
8
|
+
"list_tilemap_layers": list_tilemap_layers,
|
|
9
|
+
"get_tilemap_layer_info": get_tilemap_layer_info,
|
|
10
|
+
"get_tileset_info": get_tileset_info,
|
|
11
|
+
"get_used_cells": get_used_cells,
|
|
12
|
+
"get_cell": get_cell,
|
|
13
|
+
"set_cell": set_cell,
|
|
14
|
+
"erase_cell": erase_cell,
|
|
15
|
+
"clear_layer": clear_layer,
|
|
16
|
+
"get_cells_in_region": get_cells_in_region,
|
|
17
|
+
"set_cells_batch": set_cells_batch,
|
|
18
|
+
"convert_coords": convert_coords,
|
|
19
|
+
"list_gridmaps": list_gridmaps,
|
|
20
|
+
"get_gridmap_info": get_gridmap_info,
|
|
21
|
+
"get_meshlib_info": get_meshlib_info,
|
|
22
|
+
"get_gridmap_used_cells": get_gridmap_used_cells,
|
|
23
|
+
"get_gridmap_cell": get_gridmap_cell,
|
|
24
|
+
"set_gridmap_cell": set_gridmap_cell,
|
|
25
|
+
"clear_gridmap_cell": clear_gridmap_cell,
|
|
26
|
+
"clear_gridmap": clear_gridmap,
|
|
27
|
+
"get_cells_by_item": get_cells_by_item,
|
|
28
|
+
"set_gridmap_cells_batch": set_gridmap_cells_batch,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
func _get_tilemap_layer(node_path: String) -> TileMapLayer:
|
|
33
|
+
var node := _get_node(node_path)
|
|
34
|
+
if not node:
|
|
35
|
+
return null
|
|
36
|
+
if not node is TileMapLayer:
|
|
37
|
+
return null
|
|
38
|
+
return node as TileMapLayer
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
func _get_gridmap(node_path: String) -> GridMap:
|
|
42
|
+
var node := _get_node(node_path)
|
|
43
|
+
if not node:
|
|
44
|
+
return null
|
|
45
|
+
if not node is GridMap:
|
|
46
|
+
return null
|
|
47
|
+
return node as GridMap
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
func _find_tilemap_layers(node: Node, result: Array) -> void:
|
|
51
|
+
if node is TileMapLayer:
|
|
52
|
+
result.append({
|
|
53
|
+
"path": str(node.get_path()),
|
|
54
|
+
"name": node.name
|
|
55
|
+
})
|
|
56
|
+
for child in node.get_children():
|
|
57
|
+
_find_tilemap_layers(child, result)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
func _find_gridmaps(node: Node, result: Array) -> void:
|
|
61
|
+
if node is GridMap:
|
|
62
|
+
result.append({
|
|
63
|
+
"path": str(node.get_path()),
|
|
64
|
+
"name": node.name
|
|
65
|
+
})
|
|
66
|
+
for child in node.get_children():
|
|
67
|
+
_find_gridmaps(child, result)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
func _serialize_vector2i(v: Vector2i) -> Dictionary:
|
|
71
|
+
return {"x": v.x, "y": v.y}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
func _serialize_vector3i(v: Vector3i) -> Dictionary:
|
|
75
|
+
return {"x": v.x, "y": v.y, "z": v.z}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
func _deserialize_vector2i(d: Dictionary) -> Vector2i:
|
|
79
|
+
return Vector2i(int(d.get("x", 0)), int(d.get("y", 0)))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
func _deserialize_vector3i(d: Dictionary) -> Vector3i:
|
|
83
|
+
return Vector3i(int(d.get("x", 0)), int(d.get("y", 0)), int(d.get("z", 0)))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
func list_tilemap_layers(params: Dictionary) -> Dictionary:
|
|
87
|
+
var root_path: String = params.get("root_path", "")
|
|
88
|
+
var root: Node
|
|
89
|
+
|
|
90
|
+
if root_path.is_empty():
|
|
91
|
+
root = EditorInterface.get_edited_scene_root()
|
|
92
|
+
else:
|
|
93
|
+
root = _get_node(root_path)
|
|
94
|
+
|
|
95
|
+
if not root:
|
|
96
|
+
return _error("NODE_NOT_FOUND", "Root node not found")
|
|
97
|
+
|
|
98
|
+
var layers := []
|
|
99
|
+
_find_tilemap_layers(root, layers)
|
|
100
|
+
|
|
101
|
+
return _success({"tilemap_layers": layers})
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
func get_tilemap_layer_info(params: Dictionary) -> Dictionary:
|
|
105
|
+
var node_path: String = params.get("node_path", "")
|
|
106
|
+
if node_path.is_empty():
|
|
107
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
108
|
+
|
|
109
|
+
var layer := _get_tilemap_layer(node_path)
|
|
110
|
+
if not layer:
|
|
111
|
+
var node := _get_node(node_path)
|
|
112
|
+
if not node:
|
|
113
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
114
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
115
|
+
|
|
116
|
+
var tileset_path := ""
|
|
117
|
+
if layer.tile_set:
|
|
118
|
+
tileset_path = layer.tile_set.resource_path
|
|
119
|
+
|
|
120
|
+
return _success({
|
|
121
|
+
"name": layer.name,
|
|
122
|
+
"enabled": layer.enabled,
|
|
123
|
+
"tileset_path": tileset_path,
|
|
124
|
+
"cell_quadrant_size": layer.rendering_quadrant_size,
|
|
125
|
+
"collision_enabled": layer.collision_enabled,
|
|
126
|
+
"used_cells_count": layer.get_used_cells().size()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
func get_tileset_info(params: Dictionary) -> Dictionary:
|
|
131
|
+
var node_path: String = params.get("node_path", "")
|
|
132
|
+
if node_path.is_empty():
|
|
133
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
134
|
+
|
|
135
|
+
var layer := _get_tilemap_layer(node_path)
|
|
136
|
+
if not layer:
|
|
137
|
+
var node := _get_node(node_path)
|
|
138
|
+
if not node:
|
|
139
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
140
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
141
|
+
|
|
142
|
+
if not layer.tile_set:
|
|
143
|
+
return _error("NO_TILESET", "TileMapLayer has no TileSet assigned")
|
|
144
|
+
|
|
145
|
+
var tileset := layer.tile_set
|
|
146
|
+
var sources := []
|
|
147
|
+
|
|
148
|
+
for i in range(tileset.get_source_count()):
|
|
149
|
+
var source_id := tileset.get_source_id(i)
|
|
150
|
+
var source := tileset.get_source(source_id)
|
|
151
|
+
var source_info := {
|
|
152
|
+
"source_id": source_id,
|
|
153
|
+
"source_type": "unknown"
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if source is TileSetAtlasSource:
|
|
157
|
+
var atlas := source as TileSetAtlasSource
|
|
158
|
+
source_info["source_type"] = "atlas"
|
|
159
|
+
source_info["texture_path"] = atlas.texture.resource_path if atlas.texture else ""
|
|
160
|
+
source_info["texture_region_size"] = _serialize_vector2i(atlas.texture_region_size)
|
|
161
|
+
source_info["tile_count"] = atlas.get_tiles_count()
|
|
162
|
+
elif source is TileSetScenesCollectionSource:
|
|
163
|
+
source_info["source_type"] = "scenes_collection"
|
|
164
|
+
var scenes_source := source as TileSetScenesCollectionSource
|
|
165
|
+
source_info["scene_count"] = scenes_source.get_scene_tiles_count()
|
|
166
|
+
|
|
167
|
+
sources.append(source_info)
|
|
168
|
+
|
|
169
|
+
return _success({
|
|
170
|
+
"tileset_path": tileset.resource_path,
|
|
171
|
+
"tile_size": _serialize_vector2i(tileset.tile_size),
|
|
172
|
+
"source_count": tileset.get_source_count(),
|
|
173
|
+
"sources": sources
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
func get_used_cells(params: Dictionary) -> Dictionary:
|
|
178
|
+
var node_path: String = params.get("node_path", "")
|
|
179
|
+
if node_path.is_empty():
|
|
180
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
181
|
+
|
|
182
|
+
var layer := _get_tilemap_layer(node_path)
|
|
183
|
+
if not layer:
|
|
184
|
+
var node := _get_node(node_path)
|
|
185
|
+
if not node:
|
|
186
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
187
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
188
|
+
|
|
189
|
+
var cells := layer.get_used_cells()
|
|
190
|
+
var result := []
|
|
191
|
+
for cell in cells:
|
|
192
|
+
result.append(_serialize_vector2i(cell))
|
|
193
|
+
|
|
194
|
+
return _success({"cells": result, "count": result.size()})
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
func get_cell(params: Dictionary) -> Dictionary:
|
|
198
|
+
var node_path: String = params.get("node_path", "")
|
|
199
|
+
if node_path.is_empty():
|
|
200
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
201
|
+
if not params.has("coords"):
|
|
202
|
+
return _error("INVALID_PARAMS", "coords is required")
|
|
203
|
+
|
|
204
|
+
var layer := _get_tilemap_layer(node_path)
|
|
205
|
+
if not layer:
|
|
206
|
+
var node := _get_node(node_path)
|
|
207
|
+
if not node:
|
|
208
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
209
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
210
|
+
|
|
211
|
+
var coords := _deserialize_vector2i(params["coords"])
|
|
212
|
+
var source_id := layer.get_cell_source_id(coords)
|
|
213
|
+
var atlas_coords := layer.get_cell_atlas_coords(coords)
|
|
214
|
+
var alt_tile := layer.get_cell_alternative_tile(coords)
|
|
215
|
+
|
|
216
|
+
if source_id == -1:
|
|
217
|
+
return _success({
|
|
218
|
+
"coords": _serialize_vector2i(coords),
|
|
219
|
+
"empty": true
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
return _success({
|
|
223
|
+
"coords": _serialize_vector2i(coords),
|
|
224
|
+
"empty": false,
|
|
225
|
+
"source_id": source_id,
|
|
226
|
+
"atlas_coords": _serialize_vector2i(atlas_coords),
|
|
227
|
+
"alternative_tile": alt_tile
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
func set_cell(params: Dictionary) -> Dictionary:
|
|
232
|
+
var node_path: String = params.get("node_path", "")
|
|
233
|
+
if node_path.is_empty():
|
|
234
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
235
|
+
if not params.has("coords"):
|
|
236
|
+
return _error("INVALID_PARAMS", "coords is required")
|
|
237
|
+
|
|
238
|
+
var layer := _get_tilemap_layer(node_path)
|
|
239
|
+
if not layer:
|
|
240
|
+
var node := _get_node(node_path)
|
|
241
|
+
if not node:
|
|
242
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
243
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
244
|
+
|
|
245
|
+
var coords := _deserialize_vector2i(params["coords"])
|
|
246
|
+
var source_id: int = params.get("source_id", 0)
|
|
247
|
+
var atlas_coords := Vector2i(0, 0)
|
|
248
|
+
if params.has("atlas_coords"):
|
|
249
|
+
atlas_coords = _deserialize_vector2i(params["atlas_coords"])
|
|
250
|
+
var alt_tile: int = params.get("alternative_tile", 0)
|
|
251
|
+
|
|
252
|
+
layer.set_cell(coords, source_id, atlas_coords, alt_tile)
|
|
253
|
+
|
|
254
|
+
return _success({
|
|
255
|
+
"coords": _serialize_vector2i(coords),
|
|
256
|
+
"source_id": source_id,
|
|
257
|
+
"atlas_coords": _serialize_vector2i(atlas_coords),
|
|
258
|
+
"alternative_tile": alt_tile
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
func erase_cell(params: Dictionary) -> Dictionary:
|
|
263
|
+
var node_path: String = params.get("node_path", "")
|
|
264
|
+
if node_path.is_empty():
|
|
265
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
266
|
+
if not params.has("coords"):
|
|
267
|
+
return _error("INVALID_PARAMS", "coords is required")
|
|
268
|
+
|
|
269
|
+
var layer := _get_tilemap_layer(node_path)
|
|
270
|
+
if not layer:
|
|
271
|
+
var node := _get_node(node_path)
|
|
272
|
+
if not node:
|
|
273
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
274
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
275
|
+
|
|
276
|
+
var coords := _deserialize_vector2i(params["coords"])
|
|
277
|
+
layer.erase_cell(coords)
|
|
278
|
+
|
|
279
|
+
return _success({"erased": _serialize_vector2i(coords)})
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
func clear_layer(params: Dictionary) -> Dictionary:
|
|
283
|
+
var node_path: String = params.get("node_path", "")
|
|
284
|
+
if node_path.is_empty():
|
|
285
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
286
|
+
|
|
287
|
+
var layer := _get_tilemap_layer(node_path)
|
|
288
|
+
if not layer:
|
|
289
|
+
var node := _get_node(node_path)
|
|
290
|
+
if not node:
|
|
291
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
292
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
293
|
+
|
|
294
|
+
var count := layer.get_used_cells().size()
|
|
295
|
+
layer.clear()
|
|
296
|
+
|
|
297
|
+
return _success({"cleared": true, "cells_removed": count})
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
func get_cells_in_region(params: Dictionary) -> Dictionary:
|
|
301
|
+
var node_path: String = params.get("node_path", "")
|
|
302
|
+
if node_path.is_empty():
|
|
303
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
304
|
+
if not params.has("min_coords"):
|
|
305
|
+
return _error("INVALID_PARAMS", "min_coords is required")
|
|
306
|
+
if not params.has("max_coords"):
|
|
307
|
+
return _error("INVALID_PARAMS", "max_coords is required")
|
|
308
|
+
|
|
309
|
+
var layer := _get_tilemap_layer(node_path)
|
|
310
|
+
if not layer:
|
|
311
|
+
var node := _get_node(node_path)
|
|
312
|
+
if not node:
|
|
313
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
314
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
315
|
+
|
|
316
|
+
var min_coords := _deserialize_vector2i(params["min_coords"])
|
|
317
|
+
var max_coords := _deserialize_vector2i(params["max_coords"])
|
|
318
|
+
|
|
319
|
+
var cells := []
|
|
320
|
+
for cell in layer.get_used_cells():
|
|
321
|
+
if cell.x >= min_coords.x and cell.x <= max_coords.x and cell.y >= min_coords.y and cell.y <= max_coords.y:
|
|
322
|
+
var source_id := layer.get_cell_source_id(cell)
|
|
323
|
+
var atlas_coords := layer.get_cell_atlas_coords(cell)
|
|
324
|
+
var alt_tile := layer.get_cell_alternative_tile(cell)
|
|
325
|
+
cells.append({
|
|
326
|
+
"coords": _serialize_vector2i(cell),
|
|
327
|
+
"source_id": source_id,
|
|
328
|
+
"atlas_coords": _serialize_vector2i(atlas_coords),
|
|
329
|
+
"alternative_tile": alt_tile
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
return _success({"cells": cells, "count": cells.size()})
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
func set_cells_batch(params: Dictionary) -> Dictionary:
|
|
336
|
+
var node_path: String = params.get("node_path", "")
|
|
337
|
+
if node_path.is_empty():
|
|
338
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
339
|
+
if not params.has("cells"):
|
|
340
|
+
return _error("INVALID_PARAMS", "cells is required")
|
|
341
|
+
|
|
342
|
+
var layer := _get_tilemap_layer(node_path)
|
|
343
|
+
if not layer:
|
|
344
|
+
var node := _get_node(node_path)
|
|
345
|
+
if not node:
|
|
346
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
347
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
348
|
+
|
|
349
|
+
var cells_data: Array = params["cells"]
|
|
350
|
+
var count := 0
|
|
351
|
+
|
|
352
|
+
for cell_data in cells_data:
|
|
353
|
+
if not cell_data.has("coords"):
|
|
354
|
+
continue
|
|
355
|
+
var coords := _deserialize_vector2i(cell_data["coords"])
|
|
356
|
+
var source_id: int = cell_data.get("source_id", 0)
|
|
357
|
+
var atlas_coords := Vector2i(0, 0)
|
|
358
|
+
if cell_data.has("atlas_coords"):
|
|
359
|
+
atlas_coords = _deserialize_vector2i(cell_data["atlas_coords"])
|
|
360
|
+
var alt_tile: int = cell_data.get("alternative_tile", 0)
|
|
361
|
+
|
|
362
|
+
layer.set_cell(coords, source_id, atlas_coords, alt_tile)
|
|
363
|
+
count += 1
|
|
364
|
+
|
|
365
|
+
return _success({"cells_set": count})
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
func convert_coords(params: Dictionary) -> Dictionary:
|
|
369
|
+
var node_path: String = params.get("node_path", "")
|
|
370
|
+
if node_path.is_empty():
|
|
371
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
372
|
+
|
|
373
|
+
var layer := _get_tilemap_layer(node_path)
|
|
374
|
+
if not layer:
|
|
375
|
+
var node := _get_node(node_path)
|
|
376
|
+
if not node:
|
|
377
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
378
|
+
return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
|
|
379
|
+
|
|
380
|
+
if params.has("local_position"):
|
|
381
|
+
var local_pos_data: Dictionary = params["local_position"]
|
|
382
|
+
var local_pos := Vector2(local_pos_data.get("x", 0.0), local_pos_data.get("y", 0.0))
|
|
383
|
+
var map_coords := layer.local_to_map(local_pos)
|
|
384
|
+
return _success({
|
|
385
|
+
"direction": "local_to_map",
|
|
386
|
+
"local_position": {"x": local_pos.x, "y": local_pos.y},
|
|
387
|
+
"map_coords": _serialize_vector2i(map_coords)
|
|
388
|
+
})
|
|
389
|
+
elif params.has("map_coords"):
|
|
390
|
+
var map_coords := _deserialize_vector2i(params["map_coords"])
|
|
391
|
+
var local_pos := layer.map_to_local(map_coords)
|
|
392
|
+
return _success({
|
|
393
|
+
"direction": "map_to_local",
|
|
394
|
+
"map_coords": _serialize_vector2i(map_coords),
|
|
395
|
+
"local_position": {"x": local_pos.x, "y": local_pos.y}
|
|
396
|
+
})
|
|
397
|
+
else:
|
|
398
|
+
return _error("INVALID_PARAMS", "Either local_position or map_coords is required")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
func list_gridmaps(params: Dictionary) -> Dictionary:
|
|
402
|
+
var root_path: String = params.get("root_path", "")
|
|
403
|
+
var root: Node
|
|
404
|
+
|
|
405
|
+
if root_path.is_empty():
|
|
406
|
+
root = EditorInterface.get_edited_scene_root()
|
|
407
|
+
else:
|
|
408
|
+
root = _get_node(root_path)
|
|
409
|
+
|
|
410
|
+
if not root:
|
|
411
|
+
return _error("NODE_NOT_FOUND", "Root node not found")
|
|
412
|
+
|
|
413
|
+
var gridmaps := []
|
|
414
|
+
_find_gridmaps(root, gridmaps)
|
|
415
|
+
|
|
416
|
+
return _success({"gridmaps": gridmaps})
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
func get_gridmap_info(params: Dictionary) -> Dictionary:
|
|
420
|
+
var node_path: String = params.get("node_path", "")
|
|
421
|
+
if node_path.is_empty():
|
|
422
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
423
|
+
|
|
424
|
+
var gridmap := _get_gridmap(node_path)
|
|
425
|
+
if not gridmap:
|
|
426
|
+
var node := _get_node(node_path)
|
|
427
|
+
if not node:
|
|
428
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
429
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
430
|
+
|
|
431
|
+
var meshlib_path := ""
|
|
432
|
+
if gridmap.mesh_library:
|
|
433
|
+
meshlib_path = gridmap.mesh_library.resource_path
|
|
434
|
+
|
|
435
|
+
return _success({
|
|
436
|
+
"name": gridmap.name,
|
|
437
|
+
"mesh_library_path": meshlib_path,
|
|
438
|
+
"cell_size": _serialize_value(gridmap.cell_size),
|
|
439
|
+
"cell_center_x": gridmap.cell_center_x,
|
|
440
|
+
"cell_center_y": gridmap.cell_center_y,
|
|
441
|
+
"cell_center_z": gridmap.cell_center_z,
|
|
442
|
+
"used_cells_count": gridmap.get_used_cells().size()
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
func get_meshlib_info(params: Dictionary) -> Dictionary:
|
|
447
|
+
var node_path: String = params.get("node_path", "")
|
|
448
|
+
if node_path.is_empty():
|
|
449
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
450
|
+
|
|
451
|
+
var gridmap := _get_gridmap(node_path)
|
|
452
|
+
if not gridmap:
|
|
453
|
+
var node := _get_node(node_path)
|
|
454
|
+
if not node:
|
|
455
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
456
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
457
|
+
|
|
458
|
+
if not gridmap.mesh_library:
|
|
459
|
+
return _error("NO_MESH_LIBRARY", "GridMap has no MeshLibrary assigned")
|
|
460
|
+
|
|
461
|
+
var meshlib := gridmap.mesh_library
|
|
462
|
+
var items := []
|
|
463
|
+
|
|
464
|
+
for i in range(meshlib.get_item_list().size()):
|
|
465
|
+
var item_id: int = meshlib.get_item_list()[i]
|
|
466
|
+
var item_name := meshlib.get_item_name(item_id)
|
|
467
|
+
var mesh := meshlib.get_item_mesh(item_id)
|
|
468
|
+
var mesh_path := mesh.resource_path if mesh else ""
|
|
469
|
+
|
|
470
|
+
items.append({
|
|
471
|
+
"index": item_id,
|
|
472
|
+
"name": item_name,
|
|
473
|
+
"mesh_path": mesh_path
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
return _success({
|
|
477
|
+
"mesh_library_path": meshlib.resource_path,
|
|
478
|
+
"item_count": meshlib.get_item_list().size(),
|
|
479
|
+
"items": items
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
func get_gridmap_used_cells(params: Dictionary) -> Dictionary:
|
|
484
|
+
var node_path: String = params.get("node_path", "")
|
|
485
|
+
if node_path.is_empty():
|
|
486
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
487
|
+
|
|
488
|
+
var gridmap := _get_gridmap(node_path)
|
|
489
|
+
if not gridmap:
|
|
490
|
+
var node := _get_node(node_path)
|
|
491
|
+
if not node:
|
|
492
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
493
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
494
|
+
|
|
495
|
+
var cells := gridmap.get_used_cells()
|
|
496
|
+
var result := []
|
|
497
|
+
for cell in cells:
|
|
498
|
+
result.append(_serialize_vector3i(cell))
|
|
499
|
+
|
|
500
|
+
return _success({"cells": result, "count": result.size()})
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
func get_gridmap_cell(params: Dictionary) -> Dictionary:
|
|
504
|
+
var node_path: String = params.get("node_path", "")
|
|
505
|
+
if node_path.is_empty():
|
|
506
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
507
|
+
if not params.has("coords"):
|
|
508
|
+
return _error("INVALID_PARAMS", "coords is required")
|
|
509
|
+
|
|
510
|
+
var gridmap := _get_gridmap(node_path)
|
|
511
|
+
if not gridmap:
|
|
512
|
+
var node := _get_node(node_path)
|
|
513
|
+
if not node:
|
|
514
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
515
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
516
|
+
|
|
517
|
+
var coords := _deserialize_vector3i(params["coords"])
|
|
518
|
+
var item := gridmap.get_cell_item(coords)
|
|
519
|
+
var orientation := gridmap.get_cell_item_orientation(coords)
|
|
520
|
+
|
|
521
|
+
if item == GridMap.INVALID_CELL_ITEM:
|
|
522
|
+
return _success({
|
|
523
|
+
"coords": _serialize_vector3i(coords),
|
|
524
|
+
"empty": true
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
var item_name := ""
|
|
528
|
+
if gridmap.mesh_library:
|
|
529
|
+
item_name = gridmap.mesh_library.get_item_name(item)
|
|
530
|
+
|
|
531
|
+
return _success({
|
|
532
|
+
"coords": _serialize_vector3i(coords),
|
|
533
|
+
"empty": false,
|
|
534
|
+
"item": item,
|
|
535
|
+
"item_name": item_name,
|
|
536
|
+
"orientation": orientation
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
func set_gridmap_cell(params: Dictionary) -> Dictionary:
|
|
541
|
+
var node_path: String = params.get("node_path", "")
|
|
542
|
+
if node_path.is_empty():
|
|
543
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
544
|
+
if not params.has("coords"):
|
|
545
|
+
return _error("INVALID_PARAMS", "coords is required")
|
|
546
|
+
if not params.has("item"):
|
|
547
|
+
return _error("INVALID_PARAMS", "item is required")
|
|
548
|
+
|
|
549
|
+
var gridmap := _get_gridmap(node_path)
|
|
550
|
+
if not gridmap:
|
|
551
|
+
var node := _get_node(node_path)
|
|
552
|
+
if not node:
|
|
553
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
554
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
555
|
+
|
|
556
|
+
var coords := _deserialize_vector3i(params["coords"])
|
|
557
|
+
var item: int = params["item"]
|
|
558
|
+
var orientation: int = params.get("orientation", 0)
|
|
559
|
+
|
|
560
|
+
gridmap.set_cell_item(coords, item, orientation)
|
|
561
|
+
|
|
562
|
+
return _success({
|
|
563
|
+
"coords": _serialize_vector3i(coords),
|
|
564
|
+
"item": item,
|
|
565
|
+
"orientation": orientation
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
func clear_gridmap_cell(params: Dictionary) -> Dictionary:
|
|
570
|
+
var node_path: String = params.get("node_path", "")
|
|
571
|
+
if node_path.is_empty():
|
|
572
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
573
|
+
if not params.has("coords"):
|
|
574
|
+
return _error("INVALID_PARAMS", "coords is required")
|
|
575
|
+
|
|
576
|
+
var gridmap := _get_gridmap(node_path)
|
|
577
|
+
if not gridmap:
|
|
578
|
+
var node := _get_node(node_path)
|
|
579
|
+
if not node:
|
|
580
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
581
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
582
|
+
|
|
583
|
+
var coords := _deserialize_vector3i(params["coords"])
|
|
584
|
+
gridmap.set_cell_item(coords, GridMap.INVALID_CELL_ITEM)
|
|
585
|
+
|
|
586
|
+
return _success({"cleared": _serialize_vector3i(coords)})
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
func clear_gridmap(params: Dictionary) -> Dictionary:
|
|
590
|
+
var node_path: String = params.get("node_path", "")
|
|
591
|
+
if node_path.is_empty():
|
|
592
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
593
|
+
|
|
594
|
+
var gridmap := _get_gridmap(node_path)
|
|
595
|
+
if not gridmap:
|
|
596
|
+
var node := _get_node(node_path)
|
|
597
|
+
if not node:
|
|
598
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
599
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
600
|
+
|
|
601
|
+
var count := gridmap.get_used_cells().size()
|
|
602
|
+
gridmap.clear()
|
|
603
|
+
|
|
604
|
+
return _success({"cleared": true, "cells_removed": count})
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
func get_cells_by_item(params: Dictionary) -> Dictionary:
|
|
608
|
+
var node_path: String = params.get("node_path", "")
|
|
609
|
+
if node_path.is_empty():
|
|
610
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
611
|
+
if not params.has("item"):
|
|
612
|
+
return _error("INVALID_PARAMS", "item is required")
|
|
613
|
+
|
|
614
|
+
var gridmap := _get_gridmap(node_path)
|
|
615
|
+
if not gridmap:
|
|
616
|
+
var node := _get_node(node_path)
|
|
617
|
+
if not node:
|
|
618
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
619
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
620
|
+
|
|
621
|
+
var item: int = params["item"]
|
|
622
|
+
var cells := gridmap.get_used_cells_by_item(item)
|
|
623
|
+
var result := []
|
|
624
|
+
for cell in cells:
|
|
625
|
+
result.append(_serialize_vector3i(cell))
|
|
626
|
+
|
|
627
|
+
return _success({"item": item, "cells": result, "count": result.size()})
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
func set_gridmap_cells_batch(params: Dictionary) -> Dictionary:
|
|
631
|
+
var node_path: String = params.get("node_path", "")
|
|
632
|
+
if node_path.is_empty():
|
|
633
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
634
|
+
if not params.has("cells"):
|
|
635
|
+
return _error("INVALID_PARAMS", "cells is required")
|
|
636
|
+
|
|
637
|
+
var gridmap := _get_gridmap(node_path)
|
|
638
|
+
if not gridmap:
|
|
639
|
+
var node := _get_node(node_path)
|
|
640
|
+
if not node:
|
|
641
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
642
|
+
return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
|
|
643
|
+
|
|
644
|
+
var cells_data: Array = params["cells"]
|
|
645
|
+
var count := 0
|
|
646
|
+
|
|
647
|
+
for cell_data in cells_data:
|
|
648
|
+
if not cell_data.has("coords") or not cell_data.has("item"):
|
|
649
|
+
continue
|
|
650
|
+
var coords := _deserialize_vector3i(cell_data["coords"])
|
|
651
|
+
var item: int = cell_data["item"]
|
|
652
|
+
var orientation: int = cell_data.get("orientation", 0)
|
|
653
|
+
|
|
654
|
+
gridmap.set_cell_item(coords, item, orientation)
|
|
655
|
+
count += 1
|
|
656
|
+
|
|
657
|
+
return _success({"cells_set": count})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://0hpst23ps31k
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPBaseCommand
|
|
3
|
+
extends RefCounted
|
|
4
|
+
|
|
5
|
+
var _plugin: EditorPlugin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
func setup(plugin: EditorPlugin) -> void:
|
|
9
|
+
_plugin = plugin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
func get_commands() -> Dictionary:
|
|
13
|
+
return {}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
func _success(result: Dictionary) -> Dictionary:
|
|
17
|
+
return MCPUtils.success(result)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
func _error(code: String, message: String) -> Dictionary:
|
|
21
|
+
return MCPUtils.error(code, message)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
func _get_node(path: String) -> Node:
|
|
25
|
+
return MCPUtils.get_node_from_path(path)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
func _serialize_value(value: Variant) -> Variant:
|
|
29
|
+
return MCPUtils.serialize_value(value)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://nr8o2h4rehka
|