gopeak 2.1.0 → 2.2.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.
@@ -0,0 +1,425 @@
1
+ @tool
2
+ extends Node
3
+ class_name MCPResourceTools
4
+
5
+ var _editor_plugin: EditorPlugin = null
6
+
7
+ func set_editor_plugin(plugin: EditorPlugin) -> void:
8
+ _editor_plugin = plugin
9
+
10
+
11
+ func _ensure_res_path(path: String) -> String:
12
+ if path.begins_with("res://"):
13
+ return path
14
+ if path.begins_with("/"):
15
+ var project_abs := ProjectSettings.globalize_path("res://")
16
+ if path.begins_with(project_abs):
17
+ var rel := path.substr(project_abs.length())
18
+ return "res://" + rel
19
+ return "res://" + path
20
+
21
+
22
+ func _refresh_filesystem() -> void:
23
+ if _editor_plugin:
24
+ EditorInterface.get_resource_filesystem().scan()
25
+
26
+
27
+ func _parse_value(value):
28
+ if typeof(value) == TYPE_DICTIONARY:
29
+ if value.has("type") or value.has("_type"):
30
+ var t = value.get("type", value.get("_type", ""))
31
+ match t:
32
+ "Vector2":
33
+ return Vector2(value.get("x", 0), value.get("y", 0))
34
+ "Vector3":
35
+ return Vector3(value.get("x", 0), value.get("y", 0), value.get("z", 0))
36
+ "Color":
37
+ return Color(value.get("r", 1), value.get("g", 1), value.get("b", 1), value.get("a", 1))
38
+ "Vector2i":
39
+ return Vector2i(value.get("x", 0), value.get("y", 0))
40
+ "Vector3i":
41
+ return Vector3i(value.get("x", 0), value.get("y", 0), value.get("z", 0))
42
+ "Rect2":
43
+ return Rect2(value.get("x", 0), value.get("y", 0), value.get("width", 0), value.get("height", 0))
44
+ "NodePath":
45
+ return NodePath(value.get("path", ""))
46
+ if typeof(value) == TYPE_ARRAY:
47
+ var result := []
48
+ for item in value:
49
+ result.append(_parse_value(item))
50
+ return result
51
+ return value
52
+
53
+
54
+ func _set_resource_properties(resource: Resource, properties) -> void:
55
+ var props = properties
56
+ if typeof(properties) == TYPE_STRING:
57
+ var json := JSON.new()
58
+ if json.parse(properties) == OK:
59
+ props = json.data
60
+ else:
61
+ return
62
+ if typeof(props) != TYPE_DICTIONARY:
63
+ return
64
+ for key in props:
65
+ var val = _parse_value(props[key])
66
+ resource.set(key, val)
67
+
68
+
69
+ func _parse_properties_dict(raw) -> Dictionary:
70
+ if typeof(raw) == TYPE_DICTIONARY:
71
+ return raw
72
+ if typeof(raw) == TYPE_STRING and raw != "":
73
+ var json := JSON.new()
74
+ if json.parse(raw) == OK and typeof(json.data) == TYPE_DICTIONARY:
75
+ return json.data
76
+ return {}
77
+
78
+
79
+ func _load_theme(theme_path: String) -> Theme:
80
+ var loaded = load(theme_path)
81
+ if loaded is Theme:
82
+ return loaded
83
+ return Theme.new()
84
+
85
+
86
+ func _save_scene_root(root: Node, scene_path: String) -> int:
87
+ var packed := PackedScene.new()
88
+ var pack_result := packed.pack(root)
89
+ if pack_result != OK:
90
+ return pack_result
91
+ return ResourceSaver.save(packed, scene_path)
92
+
93
+
94
+ func create_resource(args: Dictionary) -> Dictionary:
95
+ var res_path := _ensure_res_path(str(args.get("resourcePath", "")))
96
+ var resource_type := str(args.get("resourceType", "Resource"))
97
+ if res_path == "res://":
98
+ return {"ok": false, "error": "resourcePath is required"}
99
+
100
+ var resource = ClassDB.instantiate(resource_type)
101
+ if resource == null or not (resource is Resource):
102
+ return {"ok": false, "error": "Failed to instantiate resource type", "resourceType": resource_type}
103
+
104
+ var script_path := str(args.get("script", ""))
105
+ if script_path != "":
106
+ var script_obj = load(_ensure_res_path(script_path))
107
+ if script_obj:
108
+ resource.set_script(script_obj)
109
+
110
+ if args.has("properties"):
111
+ _set_resource_properties(resource, args.get("properties"))
112
+
113
+ var save_result := ResourceSaver.save(resource, res_path)
114
+ if save_result != OK:
115
+ return {"ok": false, "error": "Failed to save resource", "code": save_result}
116
+
117
+ _refresh_filesystem()
118
+ return {
119
+ "ok": true,
120
+ "resourcePath": res_path,
121
+ "resourceType": args.get("resourceType")
122
+ }
123
+
124
+
125
+ func modify_resource(args: Dictionary) -> Dictionary:
126
+ var res_path := _ensure_res_path(str(args.get("resourcePath", "")))
127
+ if res_path == "res://":
128
+ return {"ok": false, "error": "resourcePath is required"}
129
+
130
+ var resource = load(res_path)
131
+ if resource == null or not (resource is Resource):
132
+ return {"ok": false, "error": "Resource not found", "resourcePath": res_path}
133
+
134
+ _set_resource_properties(resource, args.get("properties", ""))
135
+ var save_result := ResourceSaver.save(resource, res_path)
136
+ if save_result != OK:
137
+ return {"ok": false, "error": "Failed to save resource", "code": save_result}
138
+
139
+ _refresh_filesystem()
140
+ return {"ok": true, "resourcePath": res_path}
141
+
142
+
143
+ func create_material(args: Dictionary) -> Dictionary:
144
+ var mat_path := _ensure_res_path(str(args.get("materialPath", "")))
145
+ var material_type := str(args.get("materialType", "StandardMaterial3D"))
146
+ if mat_path == "res://":
147
+ return {"ok": false, "error": "materialPath is required"}
148
+
149
+ var material = ClassDB.instantiate(material_type)
150
+ if material == null or not (material is Material):
151
+ return {"ok": false, "error": "Failed to instantiate material type", "materialType": material_type}
152
+
153
+ if material is ShaderMaterial:
154
+ var shader_path := str(args.get("shader", ""))
155
+ if shader_path != "":
156
+ var shader_res = load(_ensure_res_path(shader_path))
157
+ if shader_res is Shader:
158
+ (material as ShaderMaterial).shader = shader_res
159
+
160
+ if args.has("properties"):
161
+ _set_resource_properties(material, args.get("properties"))
162
+
163
+ var save_result := ResourceSaver.save(material, mat_path)
164
+ if save_result != OK:
165
+ return {"ok": false, "error": "Failed to save material", "code": save_result}
166
+
167
+ _refresh_filesystem()
168
+ return {
169
+ "ok": true,
170
+ "materialPath": mat_path,
171
+ "materialType": material_type
172
+ }
173
+
174
+
175
+ func create_shader(args: Dictionary) -> Dictionary:
176
+ var shader_path := _ensure_res_path(str(args.get("shaderPath", "")))
177
+ var shader_type := str(args.get("shaderType", "canvas_item"))
178
+ if shader_path == "res://":
179
+ return {"ok": false, "error": "shaderPath is required"}
180
+
181
+ var code := str(args.get("code", ""))
182
+ var template := str(args.get("template", ""))
183
+
184
+ if code == "":
185
+ if template != "":
186
+ match template:
187
+ "basic":
188
+ code = "shader_type %s;\n\nvoid fragment() {\n\tCOLOR = vec4(1.0);\n}\n" % shader_type
189
+ "color_shift":
190
+ code = "shader_type %s;\n\nuniform vec4 color_shift : source_color = vec4(0.1, 0.0, 0.2, 0.0);\n\nvoid fragment() {\n\tvec4 base = texture(TEXTURE, UV);\n\tCOLOR = vec4(clamp(base.rgb + color_shift.rgb, 0.0, 1.0), base.a);\n}\n" % shader_type
191
+ "outline":
192
+ code = "shader_type %s;\n\nuniform vec4 outline_color : source_color = vec4(0.0, 0.0, 0.0, 1.0);\nuniform float outline_width : hint_range(0.0, 8.0) = 1.0;\n\nvoid fragment() {\n\tvec2 px = TEXTURE_PIXEL_SIZE * outline_width;\n\tfloat a = texture(TEXTURE, UV).a;\n\tfloat edge = max(max(texture(TEXTURE, UV + vec2(px.x, 0.0)).a, texture(TEXTURE, UV - vec2(px.x, 0.0)).a), max(texture(TEXTURE, UV + vec2(0.0, px.y)).a, texture(TEXTURE, UV - vec2(0.0, px.y)).a));\n\tvec4 base = texture(TEXTURE, UV);\n\tCOLOR = mix(outline_color * edge, base, a);\n}\n" % shader_type
193
+ _:
194
+ code = "shader_type %s;\n\nvoid fragment() {\n}\n" % shader_type
195
+ else:
196
+ code = "shader_type %s;\n\nvoid fragment() {\n}\n" % shader_type
197
+
198
+ var file := FileAccess.open(shader_path, FileAccess.WRITE)
199
+ if file == null:
200
+ return {"ok": false, "error": "Failed to open shader file for writing", "shaderPath": shader_path}
201
+ file.store_string(code)
202
+ file.close()
203
+
204
+ _refresh_filesystem()
205
+ return {"ok": true, "shaderPath": shader_path, "shaderType": shader_type}
206
+
207
+
208
+ func create_tileset(args: Dictionary) -> Dictionary:
209
+ var tileset_path := _ensure_res_path(str(args.get("tilesetPath", "")))
210
+ if tileset_path == "res://":
211
+ return {"ok": false, "error": "tilesetPath is required"}
212
+
213
+ var tileset := TileSet.new()
214
+ var sources = args.get("sources", [])
215
+ if typeof(sources) != TYPE_ARRAY:
216
+ sources = []
217
+
218
+ for source in sources:
219
+ if typeof(source) != TYPE_DICTIONARY:
220
+ continue
221
+ var atlas := TileSetAtlasSource.new()
222
+ var tex_path := _ensure_res_path(str(source.get("texture", "")))
223
+ var tex = load(tex_path)
224
+ if tex == null:
225
+ continue
226
+ atlas.texture = tex
227
+
228
+ var tile_size: Dictionary = source.get("tileSize", {})
229
+ atlas.texture_region_size = Vector2i(int(tile_size.get("x", 0)), int(tile_size.get("y", 0)))
230
+
231
+ if source.has("separation"):
232
+ var sep: Dictionary = source.get("separation", {})
233
+ atlas.separation = Vector2i(int(sep.get("x", 0)), int(sep.get("y", 0)))
234
+
235
+ if source.has("offset"):
236
+ var off: Dictionary = source.get("offset", {})
237
+ atlas.margins = Vector2i(int(off.get("x", 0)), int(off.get("y", 0)))
238
+
239
+ tileset.add_source(atlas)
240
+
241
+ var save_result := ResourceSaver.save(tileset, tileset_path)
242
+ if save_result != OK:
243
+ return {"ok": false, "error": "Failed to save TileSet", "code": save_result}
244
+
245
+ _refresh_filesystem()
246
+ return {"ok": true, "tilesetPath": tileset_path}
247
+
248
+
249
+ func set_tilemap_cells(args: Dictionary) -> Dictionary:
250
+ var scene_path := _ensure_res_path(str(args.get("scenePath", "")))
251
+ var node_path := str(args.get("tilemapNodePath", ""))
252
+ if scene_path == "res://":
253
+ return {"ok": false, "error": "scenePath is required"}
254
+
255
+ var scene_res = load(scene_path)
256
+ if scene_res == null or not (scene_res is PackedScene):
257
+ return {"ok": false, "error": "Scene not found", "scenePath": scene_path}
258
+
259
+ var root := (scene_res as PackedScene).instantiate()
260
+ if root == null:
261
+ return {"ok": false, "error": "Failed to instantiate scene"}
262
+
263
+ var tilemap: TileMap = null
264
+ if node_path == "." or node_path == "":
265
+ tilemap = root as TileMap
266
+ else:
267
+ tilemap = root.get_node_or_null(node_path) as TileMap
268
+
269
+ if tilemap == null:
270
+ root.queue_free()
271
+ return {"ok": false, "error": "TileMap node not found", "tilemapNodePath": node_path}
272
+
273
+ var layer := int(args.get("layer", 0))
274
+ var cells = args.get("cells", [])
275
+ if typeof(cells) != TYPE_ARRAY:
276
+ cells = []
277
+
278
+ for cell in cells:
279
+ if typeof(cell) != TYPE_DICTIONARY:
280
+ continue
281
+ var coords: Dictionary = cell.get("coords", {})
282
+ var atlas_coords: Dictionary = cell.get("atlasCoords", {})
283
+ tilemap.set_cell(
284
+ layer,
285
+ Vector2i(int(coords.get("x", 0)), int(coords.get("y", 0))),
286
+ int(cell.get("sourceId", -1)),
287
+ Vector2i(int(atlas_coords.get("x", 0)), int(atlas_coords.get("y", 0))),
288
+ int(cell.get("alternativeTile", 0))
289
+ )
290
+
291
+ var save_result := _save_scene_root(root, scene_path)
292
+ root.queue_free()
293
+ if save_result != OK:
294
+ return {"ok": false, "error": "Failed to save scene", "code": save_result}
295
+
296
+ _refresh_filesystem()
297
+ return {"ok": true, "cellCount": cells.size()}
298
+
299
+
300
+ func set_theme_color(args: Dictionary) -> Dictionary:
301
+ var theme_path := _ensure_res_path(str(args.get("themePath", "")))
302
+ if theme_path == "res://":
303
+ return {"ok": false, "error": "themePath is required"}
304
+
305
+ var theme := _load_theme(theme_path)
306
+ var c: Dictionary = args.get("color", {})
307
+ var color := Color(float(c.get("r", 1.0)), float(c.get("g", 1.0)), float(c.get("b", 1.0)), float(c.get("a", 1.0)))
308
+ theme.set_color(str(args.get("colorName", "")), str(args.get("controlType", "")), color)
309
+
310
+ var save_result := ResourceSaver.save(theme, theme_path)
311
+ if save_result != OK:
312
+ return {"ok": false, "error": "Failed to save theme", "code": save_result}
313
+
314
+ _refresh_filesystem()
315
+ return {"ok": true}
316
+
317
+
318
+ func set_theme_font_size(args: Dictionary) -> Dictionary:
319
+ var theme_path := _ensure_res_path(str(args.get("themePath", "")))
320
+ if theme_path == "res://":
321
+ return {"ok": false, "error": "themePath is required"}
322
+
323
+ var theme := _load_theme(theme_path)
324
+ theme.set_font_size(str(args.get("fontSizeName", "")), str(args.get("controlType", "")), int(args.get("size", 0)))
325
+
326
+ var save_result := ResourceSaver.save(theme, theme_path)
327
+ if save_result != OK:
328
+ return {"ok": false, "error": "Failed to save theme", "code": save_result}
329
+
330
+ _refresh_filesystem()
331
+ return {"ok": true}
332
+
333
+
334
+ func _get_theme_shader_code(theme: String, effect: String) -> String:
335
+ var base_color := "vec3(0.8, 0.8, 0.8)"
336
+ match theme:
337
+ "medieval":
338
+ base_color = "vec3(0.52, 0.42, 0.30)"
339
+ "cyberpunk":
340
+ base_color = "vec3(0.08, 0.12, 0.22)"
341
+ "nature":
342
+ base_color = "vec3(0.20, 0.44, 0.24)"
343
+ "scifi":
344
+ base_color = "vec3(0.35, 0.55, 0.72)"
345
+ "horror":
346
+ base_color = "vec3(0.12, 0.05, 0.08)"
347
+ "cartoon":
348
+ base_color = "vec3(0.95, 0.65, 0.20)"
349
+
350
+ var effect_block := "ALBEDO = base_col;"
351
+ match effect:
352
+ "glow":
353
+ effect_block = "ALBEDO = base_col; EMISSION = base_col * (0.5 + 0.5 * sin(TIME * 2.0));"
354
+ "hologram":
355
+ effect_block = "float scan = sin(UV.y * 120.0 + TIME * 6.0) * 0.5 + 0.5; ALBEDO = base_col * 0.5; EMISSION = base_col * (0.8 + scan); ALPHA = 0.65;"
356
+ "wind_sway":
357
+ effect_block = "ALBEDO = base_col + vec3(0.05 * sin(TIME + UV.x * 10.0));"
358
+ "torch_fire":
359
+ effect_block = "float flicker = 0.8 + 0.2 * sin(TIME * 17.0 + UV.y * 13.0); ALBEDO = base_col * flicker; EMISSION = vec3(1.0, 0.5, 0.1) * (flicker - 0.6);"
360
+ "dissolve":
361
+ effect_block = "float n = fract(sin(dot(UV * 123.4, vec2(12.9898, 78.233))) * 43758.5453); float cut = 0.45 + 0.25 * sin(TIME); ALBEDO = base_col; ALPHA = step(cut, n); EMISSION = vec3(1.0, 0.4, 0.1) * step(cut - 0.03, n) * (1.0 - step(cut + 0.03, n));"
362
+ "outline":
363
+ effect_block = "float e = abs(sin(UV.x * 80.0)) * abs(sin(UV.y * 80.0)); ALBEDO = mix(base_col, vec3(0.0), step(0.85, e));"
364
+ _:
365
+ effect_block = "ALBEDO = base_col;"
366
+
367
+ return "shader_type spatial;\nrender_mode cull_back, depth_draw_opaque;\n\nvoid fragment() {\n\tvec3 base_col = %s;\n\t%s\n}\n" % [base_color, effect_block]
368
+
369
+
370
+ func apply_theme_shader(args: Dictionary) -> Dictionary:
371
+ var scene_path := _ensure_res_path(str(args.get("scenePath", "")))
372
+ var node_path := str(args.get("nodePath", ""))
373
+ var theme := str(args.get("theme", "nature"))
374
+ var effect := str(args.get("effect", "none"))
375
+ if scene_path == "res://":
376
+ return {"ok": false, "error": "scenePath is required"}
377
+
378
+ var scene_res = load(scene_path)
379
+ if scene_res == null or not (scene_res is PackedScene):
380
+ return {"ok": false, "error": "Scene not found", "scenePath": scene_path}
381
+
382
+ var root := (scene_res as PackedScene).instantiate()
383
+ if root == null:
384
+ return {"ok": false, "error": "Failed to instantiate scene"}
385
+
386
+ var target: Node = null
387
+ if node_path == "." or node_path == "":
388
+ target = root
389
+ else:
390
+ target = root.get_node_or_null(node_path)
391
+
392
+ if target == null:
393
+ root.queue_free()
394
+ return {"ok": false, "error": "Target node not found", "nodePath": node_path}
395
+
396
+ var shader := Shader.new()
397
+ shader.code = _get_theme_shader_code(theme, effect)
398
+ var material := ShaderMaterial.new()
399
+ material.shader = shader
400
+
401
+ var params := _parse_properties_dict(args.get("shaderParams", ""))
402
+ for key in params:
403
+ material.set_shader_parameter(str(key), _parse_value(params[key]))
404
+
405
+ if target is MeshInstance3D:
406
+ (target as MeshInstance3D).material_override = material
407
+ elif target is Sprite2D:
408
+ (target as Sprite2D).material = material
409
+ elif target is Sprite3D:
410
+ (target as Sprite3D).material_override = material
411
+ elif target is CanvasItem:
412
+ (target as CanvasItem).material = material
413
+ elif target is GeometryInstance3D:
414
+ (target as GeometryInstance3D).material_override = material
415
+ else:
416
+ root.queue_free()
417
+ return {"ok": false, "error": "Unsupported node type for material application", "nodePath": node_path}
418
+
419
+ var save_result := _save_scene_root(root, scene_path)
420
+ root.queue_free()
421
+ if save_result != OK:
422
+ return {"ok": false, "error": "Failed to save scene", "code": save_result}
423
+
424
+ _refresh_filesystem()
425
+ return {"ok": true, "theme": theme, "effect": effect}