@satelliteoflove/godot-mcp 2.3.0 → 2.4.1
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 +58 -27
- 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 +255 -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 +162 -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 +58 -0
- package/addon/core/base_command.gd.uid +1 -0
- package/addon/core/mcp_debugger_plugin.gd +149 -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 +129 -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/core/types.d.ts +5 -0
- package/dist/core/types.d.ts.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/editor.d.ts.map +1 -1
- package/dist/tools/editor.js.map +1 -1
- 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/tools/scene3d.d.ts.map +1 -1
- package/dist/tools/scene3d.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,633 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
extends MCPBaseCommand
|
|
3
|
+
class_name MCPAnimationCommands
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const TRACK_TYPE_MAP := {
|
|
7
|
+
"value": Animation.TYPE_VALUE,
|
|
8
|
+
"position_3d": Animation.TYPE_POSITION_3D,
|
|
9
|
+
"rotation_3d": Animation.TYPE_ROTATION_3D,
|
|
10
|
+
"scale_3d": Animation.TYPE_SCALE_3D,
|
|
11
|
+
"blend_shape": Animation.TYPE_BLEND_SHAPE,
|
|
12
|
+
"method": Animation.TYPE_METHOD,
|
|
13
|
+
"bezier": Animation.TYPE_BEZIER,
|
|
14
|
+
"audio": Animation.TYPE_AUDIO,
|
|
15
|
+
"animation": Animation.TYPE_ANIMATION
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const LOOP_MODE_MAP := {
|
|
19
|
+
"none": Animation.LOOP_NONE,
|
|
20
|
+
"linear": Animation.LOOP_LINEAR,
|
|
21
|
+
"pingpong": Animation.LOOP_PINGPONG
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
func get_commands() -> Dictionary:
|
|
26
|
+
return {
|
|
27
|
+
"list_animation_players": list_animation_players,
|
|
28
|
+
"get_animation_player_info": get_animation_player_info,
|
|
29
|
+
"get_animation_details": get_animation_details,
|
|
30
|
+
"get_track_keyframes": get_track_keyframes,
|
|
31
|
+
"play_animation": play_animation,
|
|
32
|
+
"stop_animation": stop_animation,
|
|
33
|
+
"seek_animation": seek_animation,
|
|
34
|
+
"create_animation": create_animation,
|
|
35
|
+
"delete_animation": delete_animation,
|
|
36
|
+
"update_animation_properties": update_animation_properties,
|
|
37
|
+
"add_animation_track": add_animation_track,
|
|
38
|
+
"remove_animation_track": remove_animation_track,
|
|
39
|
+
"add_keyframe": add_keyframe,
|
|
40
|
+
"remove_keyframe": remove_keyframe,
|
|
41
|
+
"update_keyframe": update_keyframe
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
func _get_animation_player(node_path: String) -> AnimationPlayer:
|
|
46
|
+
var node := _get_node(node_path)
|
|
47
|
+
if not node:
|
|
48
|
+
return null
|
|
49
|
+
if not node is AnimationPlayer:
|
|
50
|
+
return null
|
|
51
|
+
return node as AnimationPlayer
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
func _get_animation(player: AnimationPlayer, anim_name: String) -> Animation:
|
|
55
|
+
if not player.has_animation(anim_name):
|
|
56
|
+
return null
|
|
57
|
+
return player.get_animation(anim_name)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
func _track_type_to_string(track_type: int) -> String:
|
|
61
|
+
for key in TRACK_TYPE_MAP:
|
|
62
|
+
if TRACK_TYPE_MAP[key] == track_type:
|
|
63
|
+
return key
|
|
64
|
+
return "unknown"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
func _loop_mode_to_string(loop_mode: int) -> String:
|
|
68
|
+
for key in LOOP_MODE_MAP:
|
|
69
|
+
if LOOP_MODE_MAP[key] == loop_mode:
|
|
70
|
+
return key
|
|
71
|
+
return "none"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
func _find_animation_players(node: Node, result: Array, root: Node) -> void:
|
|
75
|
+
if node is AnimationPlayer:
|
|
76
|
+
var relative_path := str(root.get_path_to(node))
|
|
77
|
+
result.append({
|
|
78
|
+
"path": relative_path,
|
|
79
|
+
"name": node.name
|
|
80
|
+
})
|
|
81
|
+
for child in node.get_children():
|
|
82
|
+
_find_animation_players(child, result, root)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
func list_animation_players(params: Dictionary) -> Dictionary:
|
|
86
|
+
var root_path: String = params.get("root_path", "")
|
|
87
|
+
var root: Node
|
|
88
|
+
|
|
89
|
+
if root_path.is_empty():
|
|
90
|
+
root = EditorInterface.get_edited_scene_root()
|
|
91
|
+
else:
|
|
92
|
+
root = _get_node(root_path)
|
|
93
|
+
|
|
94
|
+
if not root:
|
|
95
|
+
return _error("NODE_NOT_FOUND", "Root node not found")
|
|
96
|
+
|
|
97
|
+
var players := []
|
|
98
|
+
_find_animation_players(root, players, root)
|
|
99
|
+
|
|
100
|
+
return _success({"animation_players": players})
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
func get_animation_player_info(params: Dictionary) -> Dictionary:
|
|
104
|
+
var node_path: String = params.get("node_path", "")
|
|
105
|
+
if node_path.is_empty():
|
|
106
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
107
|
+
|
|
108
|
+
var player := _get_animation_player(node_path)
|
|
109
|
+
if not player:
|
|
110
|
+
var node := _get_node(node_path)
|
|
111
|
+
if not node:
|
|
112
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
113
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer: %s" % node_path)
|
|
114
|
+
|
|
115
|
+
var libraries := {}
|
|
116
|
+
for lib_name in player.get_animation_library_list():
|
|
117
|
+
var lib := player.get_animation_library(lib_name)
|
|
118
|
+
libraries[lib_name] = Array(lib.get_animation_list())
|
|
119
|
+
|
|
120
|
+
return _success({
|
|
121
|
+
"current_animation": player.current_animation,
|
|
122
|
+
"is_playing": player.is_playing(),
|
|
123
|
+
"current_position": player.current_animation_position,
|
|
124
|
+
"speed_scale": player.speed_scale,
|
|
125
|
+
"libraries": libraries,
|
|
126
|
+
"animation_count": player.get_animation_list().size()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
func get_animation_details(params: Dictionary) -> Dictionary:
|
|
131
|
+
var node_path: String = params.get("node_path", "")
|
|
132
|
+
var anim_name: String = params.get("animation_name", "")
|
|
133
|
+
|
|
134
|
+
if node_path.is_empty():
|
|
135
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
136
|
+
if anim_name.is_empty():
|
|
137
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
138
|
+
|
|
139
|
+
var player := _get_animation_player(node_path)
|
|
140
|
+
if not player:
|
|
141
|
+
var node := _get_node(node_path)
|
|
142
|
+
if not node:
|
|
143
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
144
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
145
|
+
|
|
146
|
+
var anim := _get_animation(player, anim_name)
|
|
147
|
+
if not anim:
|
|
148
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
149
|
+
|
|
150
|
+
var tracks := []
|
|
151
|
+
for i in range(anim.get_track_count()):
|
|
152
|
+
tracks.append({
|
|
153
|
+
"index": i,
|
|
154
|
+
"type": _track_type_to_string(anim.track_get_type(i)),
|
|
155
|
+
"path": str(anim.track_get_path(i)),
|
|
156
|
+
"interpolation": anim.track_get_interpolation_type(i),
|
|
157
|
+
"keyframe_count": anim.track_get_key_count(i)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
var lib_name := ""
|
|
161
|
+
var pure_name := anim_name
|
|
162
|
+
if "/" in anim_name:
|
|
163
|
+
var parts := anim_name.split("/", true, 1)
|
|
164
|
+
lib_name = parts[0]
|
|
165
|
+
pure_name = parts[1]
|
|
166
|
+
|
|
167
|
+
return _success({
|
|
168
|
+
"name": pure_name,
|
|
169
|
+
"library": lib_name,
|
|
170
|
+
"length": anim.length,
|
|
171
|
+
"loop_mode": _loop_mode_to_string(anim.loop_mode),
|
|
172
|
+
"step": anim.step,
|
|
173
|
+
"track_count": anim.get_track_count(),
|
|
174
|
+
"tracks": tracks
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
func get_track_keyframes(params: Dictionary) -> Dictionary:
|
|
179
|
+
var node_path: String = params.get("node_path", "")
|
|
180
|
+
var anim_name: String = params.get("animation_name", "")
|
|
181
|
+
var track_index: int = params.get("track_index", -1)
|
|
182
|
+
|
|
183
|
+
if node_path.is_empty():
|
|
184
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
185
|
+
if anim_name.is_empty():
|
|
186
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
187
|
+
if track_index < 0:
|
|
188
|
+
return _error("INVALID_PARAMS", "track_index is required")
|
|
189
|
+
|
|
190
|
+
var player := _get_animation_player(node_path)
|
|
191
|
+
if not player:
|
|
192
|
+
var node := _get_node(node_path)
|
|
193
|
+
if not node:
|
|
194
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
195
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
196
|
+
|
|
197
|
+
var anim := _get_animation(player, anim_name)
|
|
198
|
+
if not anim:
|
|
199
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
200
|
+
|
|
201
|
+
if track_index >= anim.get_track_count():
|
|
202
|
+
return _error("TRACK_NOT_FOUND", "Track index out of range: %d" % track_index)
|
|
203
|
+
|
|
204
|
+
var keyframes := []
|
|
205
|
+
var track_type := anim.track_get_type(track_index)
|
|
206
|
+
|
|
207
|
+
for i in range(anim.track_get_key_count(track_index)):
|
|
208
|
+
var kf := {
|
|
209
|
+
"time": anim.track_get_key_time(track_index, i),
|
|
210
|
+
"transition": anim.track_get_key_transition(track_index, i)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
match track_type:
|
|
214
|
+
Animation.TYPE_METHOD:
|
|
215
|
+
kf["method"] = anim.method_track_get_name(track_index, i)
|
|
216
|
+
kf["args"] = anim.method_track_get_params(track_index, i)
|
|
217
|
+
Animation.TYPE_BEZIER:
|
|
218
|
+
kf["value"] = anim.bezier_track_get_key_value(track_index, i)
|
|
219
|
+
kf["in_handle"] = _serialize_value(anim.bezier_track_get_key_in_handle(track_index, i))
|
|
220
|
+
kf["out_handle"] = _serialize_value(anim.bezier_track_get_key_out_handle(track_index, i))
|
|
221
|
+
_:
|
|
222
|
+
kf["value"] = _serialize_value(anim.track_get_key_value(track_index, i))
|
|
223
|
+
|
|
224
|
+
keyframes.append(kf)
|
|
225
|
+
|
|
226
|
+
return _success({
|
|
227
|
+
"track_path": str(anim.track_get_path(track_index)),
|
|
228
|
+
"track_type": _track_type_to_string(track_type),
|
|
229
|
+
"keyframes": keyframes
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
func play_animation(params: Dictionary) -> Dictionary:
|
|
234
|
+
var node_path: String = params.get("node_path", "")
|
|
235
|
+
var anim_name: String = params.get("animation_name", "")
|
|
236
|
+
var custom_blend: float = params.get("custom_blend", -1.0)
|
|
237
|
+
var custom_speed: float = params.get("custom_speed", 1.0)
|
|
238
|
+
var from_end: bool = params.get("from_end", false)
|
|
239
|
+
|
|
240
|
+
if node_path.is_empty():
|
|
241
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
242
|
+
if anim_name.is_empty():
|
|
243
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
244
|
+
|
|
245
|
+
var player := _get_animation_player(node_path)
|
|
246
|
+
if not player:
|
|
247
|
+
var node := _get_node(node_path)
|
|
248
|
+
if not node:
|
|
249
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
250
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
251
|
+
|
|
252
|
+
if not player.has_animation(anim_name):
|
|
253
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
254
|
+
|
|
255
|
+
player.play(anim_name, custom_blend, custom_speed, from_end)
|
|
256
|
+
|
|
257
|
+
return _success({"playing": anim_name, "from_position": player.current_animation_position})
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
func stop_animation(params: Dictionary) -> Dictionary:
|
|
261
|
+
var node_path: String = params.get("node_path", "")
|
|
262
|
+
var keep_state: bool = params.get("keep_state", false)
|
|
263
|
+
|
|
264
|
+
if node_path.is_empty():
|
|
265
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
266
|
+
|
|
267
|
+
var player := _get_animation_player(node_path)
|
|
268
|
+
if not player:
|
|
269
|
+
var node := _get_node(node_path)
|
|
270
|
+
if not node:
|
|
271
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
272
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
273
|
+
|
|
274
|
+
player.stop(keep_state)
|
|
275
|
+
|
|
276
|
+
return _success({"stopped": true})
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
func seek_animation(params: Dictionary) -> Dictionary:
|
|
280
|
+
var node_path: String = params.get("node_path", "")
|
|
281
|
+
var seconds: float = params.get("seconds", 0.0)
|
|
282
|
+
var update: bool = params.get("update", true)
|
|
283
|
+
|
|
284
|
+
if node_path.is_empty():
|
|
285
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
286
|
+
if not params.has("seconds"):
|
|
287
|
+
return _error("INVALID_PARAMS", "seconds is required")
|
|
288
|
+
|
|
289
|
+
var player := _get_animation_player(node_path)
|
|
290
|
+
if not player:
|
|
291
|
+
var node := _get_node(node_path)
|
|
292
|
+
if not node:
|
|
293
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
294
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
295
|
+
|
|
296
|
+
player.seek(seconds, update)
|
|
297
|
+
|
|
298
|
+
return _success({"position": player.current_animation_position})
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
func create_animation(params: Dictionary) -> Dictionary:
|
|
302
|
+
var node_path: String = params.get("node_path", "")
|
|
303
|
+
var anim_name: String = params.get("animation_name", "")
|
|
304
|
+
var lib_name: String = params.get("library_name", "")
|
|
305
|
+
var length: float = params.get("length", 1.0)
|
|
306
|
+
var loop_mode: String = params.get("loop_mode", "none")
|
|
307
|
+
var step: float = params.get("step", 0.1)
|
|
308
|
+
|
|
309
|
+
if node_path.is_empty():
|
|
310
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
311
|
+
if anim_name.is_empty():
|
|
312
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
313
|
+
|
|
314
|
+
var player := _get_animation_player(node_path)
|
|
315
|
+
if not player:
|
|
316
|
+
var node := _get_node(node_path)
|
|
317
|
+
if not node:
|
|
318
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
319
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
320
|
+
|
|
321
|
+
var lib: AnimationLibrary
|
|
322
|
+
if player.has_animation_library(lib_name):
|
|
323
|
+
lib = player.get_animation_library(lib_name)
|
|
324
|
+
else:
|
|
325
|
+
lib = AnimationLibrary.new()
|
|
326
|
+
player.add_animation_library(lib_name, lib)
|
|
327
|
+
|
|
328
|
+
if lib.has_animation(anim_name):
|
|
329
|
+
return _error("ANIMATION_EXISTS", "Animation already exists: %s" % anim_name)
|
|
330
|
+
|
|
331
|
+
var anim := Animation.new()
|
|
332
|
+
anim.length = length
|
|
333
|
+
if LOOP_MODE_MAP.has(loop_mode):
|
|
334
|
+
anim.loop_mode = LOOP_MODE_MAP[loop_mode]
|
|
335
|
+
anim.step = step
|
|
336
|
+
|
|
337
|
+
var err := lib.add_animation(anim_name, anim)
|
|
338
|
+
if err != OK:
|
|
339
|
+
return _error("CREATE_FAILED", "Failed to create animation: %s" % error_string(err))
|
|
340
|
+
|
|
341
|
+
return _success({"created": anim_name, "library": lib_name})
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
func delete_animation(params: Dictionary) -> Dictionary:
|
|
345
|
+
var node_path: String = params.get("node_path", "")
|
|
346
|
+
var anim_name: String = params.get("animation_name", "")
|
|
347
|
+
var lib_name: String = params.get("library_name", "")
|
|
348
|
+
|
|
349
|
+
if node_path.is_empty():
|
|
350
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
351
|
+
if anim_name.is_empty():
|
|
352
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
353
|
+
|
|
354
|
+
var player := _get_animation_player(node_path)
|
|
355
|
+
if not player:
|
|
356
|
+
var node := _get_node(node_path)
|
|
357
|
+
if not node:
|
|
358
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
359
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
360
|
+
|
|
361
|
+
if not player.has_animation_library(lib_name):
|
|
362
|
+
return _error("LIBRARY_NOT_FOUND", "Animation library not found: %s" % lib_name)
|
|
363
|
+
|
|
364
|
+
var lib := player.get_animation_library(lib_name)
|
|
365
|
+
if not lib.has_animation(anim_name):
|
|
366
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
367
|
+
|
|
368
|
+
lib.remove_animation(anim_name)
|
|
369
|
+
|
|
370
|
+
return _success({"deleted": anim_name})
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
func update_animation_properties(params: Dictionary) -> Dictionary:
|
|
374
|
+
var node_path: String = params.get("node_path", "")
|
|
375
|
+
var anim_name: String = params.get("animation_name", "")
|
|
376
|
+
|
|
377
|
+
if node_path.is_empty():
|
|
378
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
379
|
+
if anim_name.is_empty():
|
|
380
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
381
|
+
|
|
382
|
+
var player := _get_animation_player(node_path)
|
|
383
|
+
if not player:
|
|
384
|
+
var node := _get_node(node_path)
|
|
385
|
+
if not node:
|
|
386
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
387
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
388
|
+
|
|
389
|
+
var anim := _get_animation(player, anim_name)
|
|
390
|
+
if not anim:
|
|
391
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
392
|
+
|
|
393
|
+
var updated := {}
|
|
394
|
+
|
|
395
|
+
if params.has("length"):
|
|
396
|
+
anim.length = params["length"]
|
|
397
|
+
updated["length"] = anim.length
|
|
398
|
+
|
|
399
|
+
if params.has("loop_mode"):
|
|
400
|
+
var loop_str: String = params["loop_mode"]
|
|
401
|
+
if LOOP_MODE_MAP.has(loop_str):
|
|
402
|
+
anim.loop_mode = LOOP_MODE_MAP[loop_str]
|
|
403
|
+
updated["loop_mode"] = loop_str
|
|
404
|
+
|
|
405
|
+
if params.has("step"):
|
|
406
|
+
anim.step = params["step"]
|
|
407
|
+
updated["step"] = anim.step
|
|
408
|
+
|
|
409
|
+
return _success({"updated": anim_name, "properties": updated})
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
func add_animation_track(params: Dictionary) -> Dictionary:
|
|
413
|
+
var node_path: String = params.get("node_path", "")
|
|
414
|
+
var anim_name: String = params.get("animation_name", "")
|
|
415
|
+
var track_type: String = params.get("track_type", "")
|
|
416
|
+
var track_path: String = params.get("track_path", "")
|
|
417
|
+
var insert_at: int = params.get("insert_at", -1)
|
|
418
|
+
|
|
419
|
+
if node_path.is_empty():
|
|
420
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
421
|
+
if anim_name.is_empty():
|
|
422
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
423
|
+
if track_type.is_empty():
|
|
424
|
+
return _error("INVALID_PARAMS", "track_type is required")
|
|
425
|
+
if track_path.is_empty():
|
|
426
|
+
return _error("INVALID_PARAMS", "track_path is required")
|
|
427
|
+
|
|
428
|
+
if not TRACK_TYPE_MAP.has(track_type):
|
|
429
|
+
return _error("INVALID_TRACK_TYPE", "Invalid track type: %s. Valid types: %s" % [track_type, ", ".join(TRACK_TYPE_MAP.keys())])
|
|
430
|
+
|
|
431
|
+
var player := _get_animation_player(node_path)
|
|
432
|
+
if not player:
|
|
433
|
+
var node := _get_node(node_path)
|
|
434
|
+
if not node:
|
|
435
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
436
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
437
|
+
|
|
438
|
+
var anim := _get_animation(player, anim_name)
|
|
439
|
+
if not anim:
|
|
440
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
441
|
+
|
|
442
|
+
var godot_track_type: int = TRACK_TYPE_MAP[track_type]
|
|
443
|
+
var track_index: int
|
|
444
|
+
|
|
445
|
+
if insert_at >= 0:
|
|
446
|
+
track_index = anim.add_track(godot_track_type, insert_at)
|
|
447
|
+
else:
|
|
448
|
+
track_index = anim.add_track(godot_track_type)
|
|
449
|
+
|
|
450
|
+
anim.track_set_path(track_index, track_path)
|
|
451
|
+
|
|
452
|
+
return _success({
|
|
453
|
+
"track_index": track_index,
|
|
454
|
+
"track_path": track_path,
|
|
455
|
+
"track_type": track_type
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
func remove_animation_track(params: Dictionary) -> Dictionary:
|
|
460
|
+
var node_path: String = params.get("node_path", "")
|
|
461
|
+
var anim_name: String = params.get("animation_name", "")
|
|
462
|
+
var track_index: int = params.get("track_index", -1)
|
|
463
|
+
|
|
464
|
+
if node_path.is_empty():
|
|
465
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
466
|
+
if anim_name.is_empty():
|
|
467
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
468
|
+
if track_index < 0:
|
|
469
|
+
return _error("INVALID_PARAMS", "track_index is required")
|
|
470
|
+
|
|
471
|
+
var player := _get_animation_player(node_path)
|
|
472
|
+
if not player:
|
|
473
|
+
var node := _get_node(node_path)
|
|
474
|
+
if not node:
|
|
475
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
476
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
477
|
+
|
|
478
|
+
var anim := _get_animation(player, anim_name)
|
|
479
|
+
if not anim:
|
|
480
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
481
|
+
|
|
482
|
+
if track_index >= anim.get_track_count():
|
|
483
|
+
return _error("TRACK_NOT_FOUND", "Track index out of range: %d" % track_index)
|
|
484
|
+
|
|
485
|
+
anim.remove_track(track_index)
|
|
486
|
+
|
|
487
|
+
return _success({"removed_track": track_index})
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
func add_keyframe(params: Dictionary) -> Dictionary:
|
|
491
|
+
var node_path: String = params.get("node_path", "")
|
|
492
|
+
var anim_name: String = params.get("animation_name", "")
|
|
493
|
+
var track_index: int = params.get("track_index", -1)
|
|
494
|
+
var time: float = params.get("time", 0.0)
|
|
495
|
+
var transition: float = params.get("transition", 1.0)
|
|
496
|
+
|
|
497
|
+
if node_path.is_empty():
|
|
498
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
499
|
+
if anim_name.is_empty():
|
|
500
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
501
|
+
if track_index < 0:
|
|
502
|
+
return _error("INVALID_PARAMS", "track_index is required")
|
|
503
|
+
if not params.has("time"):
|
|
504
|
+
return _error("INVALID_PARAMS", "time is required")
|
|
505
|
+
if not params.has("value"):
|
|
506
|
+
return _error("INVALID_PARAMS", "value is required")
|
|
507
|
+
|
|
508
|
+
var player := _get_animation_player(node_path)
|
|
509
|
+
if not player:
|
|
510
|
+
var node := _get_node(node_path)
|
|
511
|
+
if not node:
|
|
512
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
513
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
514
|
+
|
|
515
|
+
var anim := _get_animation(player, anim_name)
|
|
516
|
+
if not anim:
|
|
517
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
518
|
+
|
|
519
|
+
if track_index >= anim.get_track_count():
|
|
520
|
+
return _error("TRACK_NOT_FOUND", "Track index out of range: %d" % track_index)
|
|
521
|
+
|
|
522
|
+
var value = MCPUtils.deserialize_value(params["value"])
|
|
523
|
+
var track_type := anim.track_get_type(track_index)
|
|
524
|
+
var key_index: int
|
|
525
|
+
|
|
526
|
+
match track_type:
|
|
527
|
+
Animation.TYPE_BEZIER:
|
|
528
|
+
key_index = anim.bezier_track_insert_key(track_index, time, value)
|
|
529
|
+
Animation.TYPE_METHOD:
|
|
530
|
+
var method_name: String = params.get("method_name", "")
|
|
531
|
+
var args: Array = params.get("args", [])
|
|
532
|
+
key_index = anim.method_track_add_key(track_index, time, method_name, args)
|
|
533
|
+
_:
|
|
534
|
+
key_index = anim.track_insert_key(track_index, time, value, transition)
|
|
535
|
+
|
|
536
|
+
return _success({
|
|
537
|
+
"keyframe_index": key_index,
|
|
538
|
+
"time": time,
|
|
539
|
+
"value": _serialize_value(value)
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
func remove_keyframe(params: Dictionary) -> Dictionary:
|
|
544
|
+
var node_path: String = params.get("node_path", "")
|
|
545
|
+
var anim_name: String = params.get("animation_name", "")
|
|
546
|
+
var track_index: int = params.get("track_index", -1)
|
|
547
|
+
var keyframe_index: int = params.get("keyframe_index", -1)
|
|
548
|
+
|
|
549
|
+
if node_path.is_empty():
|
|
550
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
551
|
+
if anim_name.is_empty():
|
|
552
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
553
|
+
if track_index < 0:
|
|
554
|
+
return _error("INVALID_PARAMS", "track_index is required")
|
|
555
|
+
if keyframe_index < 0:
|
|
556
|
+
return _error("INVALID_PARAMS", "keyframe_index is required")
|
|
557
|
+
|
|
558
|
+
var player := _get_animation_player(node_path)
|
|
559
|
+
if not player:
|
|
560
|
+
var node := _get_node(node_path)
|
|
561
|
+
if not node:
|
|
562
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
563
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
564
|
+
|
|
565
|
+
var anim := _get_animation(player, anim_name)
|
|
566
|
+
if not anim:
|
|
567
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
568
|
+
|
|
569
|
+
if track_index >= anim.get_track_count():
|
|
570
|
+
return _error("TRACK_NOT_FOUND", "Track index out of range: %d" % track_index)
|
|
571
|
+
|
|
572
|
+
if keyframe_index >= anim.track_get_key_count(track_index):
|
|
573
|
+
return _error("KEYFRAME_NOT_FOUND", "Keyframe index out of range: %d" % keyframe_index)
|
|
574
|
+
|
|
575
|
+
anim.track_remove_key(track_index, keyframe_index)
|
|
576
|
+
|
|
577
|
+
return _success({"removed_keyframe": keyframe_index, "track_index": track_index})
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
func update_keyframe(params: Dictionary) -> Dictionary:
|
|
581
|
+
var node_path: String = params.get("node_path", "")
|
|
582
|
+
var anim_name: String = params.get("animation_name", "")
|
|
583
|
+
var track_index: int = params.get("track_index", -1)
|
|
584
|
+
var keyframe_index: int = params.get("keyframe_index", -1)
|
|
585
|
+
|
|
586
|
+
if node_path.is_empty():
|
|
587
|
+
return _error("INVALID_PARAMS", "node_path is required")
|
|
588
|
+
if anim_name.is_empty():
|
|
589
|
+
return _error("INVALID_PARAMS", "animation_name is required")
|
|
590
|
+
if track_index < 0:
|
|
591
|
+
return _error("INVALID_PARAMS", "track_index is required")
|
|
592
|
+
if keyframe_index < 0:
|
|
593
|
+
return _error("INVALID_PARAMS", "keyframe_index is required")
|
|
594
|
+
|
|
595
|
+
var player := _get_animation_player(node_path)
|
|
596
|
+
if not player:
|
|
597
|
+
var node := _get_node(node_path)
|
|
598
|
+
if not node:
|
|
599
|
+
return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
|
|
600
|
+
return _error("NOT_ANIMATION_PLAYER", "Node is not an AnimationPlayer")
|
|
601
|
+
|
|
602
|
+
var anim := _get_animation(player, anim_name)
|
|
603
|
+
if not anim:
|
|
604
|
+
return _error("ANIMATION_NOT_FOUND", "Animation not found: %s" % anim_name)
|
|
605
|
+
|
|
606
|
+
if track_index >= anim.get_track_count():
|
|
607
|
+
return _error("TRACK_NOT_FOUND", "Track index out of range: %d" % track_index)
|
|
608
|
+
|
|
609
|
+
if keyframe_index >= anim.track_get_key_count(track_index):
|
|
610
|
+
return _error("KEYFRAME_NOT_FOUND", "Keyframe index out of range: %d" % keyframe_index)
|
|
611
|
+
|
|
612
|
+
var result := {}
|
|
613
|
+
|
|
614
|
+
if params.has("time"):
|
|
615
|
+
var new_time: float = params["time"]
|
|
616
|
+
var old_value = anim.track_get_key_value(track_index, keyframe_index)
|
|
617
|
+
var old_transition := anim.track_get_key_transition(track_index, keyframe_index)
|
|
618
|
+
anim.track_remove_key(track_index, keyframe_index)
|
|
619
|
+
keyframe_index = anim.track_insert_key(track_index, new_time, old_value, old_transition)
|
|
620
|
+
result["time"] = new_time
|
|
621
|
+
result["keyframe_index"] = keyframe_index
|
|
622
|
+
|
|
623
|
+
if params.has("value"):
|
|
624
|
+
var new_value = MCPUtils.deserialize_value(params["value"])
|
|
625
|
+
anim.track_set_key_value(track_index, keyframe_index, new_value)
|
|
626
|
+
result["value"] = _serialize_value(new_value)
|
|
627
|
+
|
|
628
|
+
if params.has("transition"):
|
|
629
|
+
var new_transition: float = params["transition"]
|
|
630
|
+
anim.track_set_key_transition(track_index, keyframe_index, new_transition)
|
|
631
|
+
result["transition"] = new_transition
|
|
632
|
+
|
|
633
|
+
return _success({"updated_keyframe": keyframe_index, "changes": result})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://bp2p0xfd1tm1h
|