2026-10-09 12:49:07 +02:00

114 lines
4.5 KiB
GDScript

@tool
extends RefCounted
## tileset.* alternatives commands: manage alternative-tile variants on an atlas
## tile — add/remove a variant and apply its flip / transpose / modulate.
##
## Stateless — every handler takes its parameters Dictionary and returns the
## response Dictionary. Persistence and the layer-node hint come from the shared
## tileset_io.gd leaf (_IO); the modulate color projection comes from the shared
## Coerce. An extracted submodule of the tileset-command group,
## reached via a `preload` alias.
const Modules := preload("res://addons/godot_mcp_toolkit/core/modules.gd")
const _IO := preload("res://addons/godot_mcp_toolkit/commands/tileset/tileset_io.gd")
const Coerce = Modules.Coerce
# -- Commands -----------------------------------------------------------------
static func cmd_add_alternative(parameters: Dictionary) -> Dictionary:
var err = MCPToolkitError.require(parameters, ["file_path", "atlas_x", "atlas_y"])
if err != null:
return err
var file_path := str(parameters.get("file_path", ""))
var source_id := int(parameters.get("source_id", 0))
var ts_or_err = _IO.load_tileset(file_path)
if ts_or_err is Dictionary:
return ts_or_err
var ts: TileSet = ts_or_err
if not ts.has_source(source_id):
return MCPToolkitError.fail("NOT_FOUND",
"No source with id %d in TileSet" % source_id)
var source = ts.get_source(source_id)
if not (source is TileSetAtlasSource):
return MCPToolkitError.fail("INVALID_CLASS",
"Source %d is not a TileSetAtlasSource" % source_id)
var atlas: TileSetAtlasSource = source
var coord := Vector2i(int(parameters["atlas_x"]), int(parameters["atlas_y"]))
if not atlas.has_tile(coord):
return MCPToolkitError.fail("NOT_FOUND",
"tile (%d,%d) not found in source %d" % [coord.x, coord.y, source_id])
var r = _apply_alternative(atlas, coord, parameters)
if r.has("error"):
return MCPToolkitError.fail("FAILED", r["error"])
var alt_id: int = r["alt_id"]
var save_result := await _IO.save_tileset(ts, file_path)
if save_result.has("error"):
return save_result
return MCPToolkitSuccess.ok({
"path": file_path,
"tile": {"atlas_x": coord.x, "atlas_y": coord.y},
"new_alternative_id": alt_id,
"hint": "Alternative %d inherits base tile properties. Customize with tileset.edit_* tools." % alt_id,
})
static func cmd_remove_alternative(parameters: Dictionary) -> Dictionary:
var err = MCPToolkitError.require(parameters, ["file_path", "atlas_x", "atlas_y", "alternative_id"])
if err != null:
return err
var file_path := str(parameters.get("file_path", ""))
var source_id := int(parameters.get("source_id", 0))
var atlas_x := int(parameters.get("atlas_x", 0))
var atlas_y := int(parameters.get("atlas_y", 0))
var alt_id := int(parameters.get("alternative_id", 0))
var ts_or_err = _IO.load_tileset(file_path)
if ts_or_err is Dictionary:
return ts_or_err
var ts: TileSet = ts_or_err
if not ts.has_source(source_id):
return MCPToolkitError.fail("NOT_FOUND",
"No source with id %d in TileSet" % source_id)
var source = ts.get_source(source_id)
if not (source is TileSetAtlasSource):
return MCPToolkitError.fail("INVALID_CLASS",
"Source %d is not a TileSetAtlasSource" % source_id)
var atlas: TileSetAtlasSource = source
var coord := Vector2i(atlas_x, atlas_y)
if not atlas.has_tile(coord):
return MCPToolkitError.fail("NOT_FOUND",
"tile (%d,%d) not found in source %d" % [atlas_x, atlas_y, source_id])
if not atlas.has_alternative_tile(coord, alt_id):
return MCPToolkitError.fail("NOT_FOUND",
"alternative %d not found for tile (%d,%d)" % [alt_id, atlas_x, atlas_y])
atlas.remove_alternative_tile(coord, alt_id)
var save_result := await _IO.save_tileset(ts, file_path)
if save_result.has("error"):
return save_result
return MCPToolkitSuccess.ok({
"path": file_path,
"removed_alternative_id": alt_id,
"tile": {"atlas_x": atlas_x, "atlas_y": atlas_y},
"hint": _IO.layer_node_hint(
"", " cells using alternative %d revert to the base tile (alternative 0). Check with tilemap.read_cells." % alt_id),
})
static func _apply_alternative(
atlas: TileSetAtlasSource, coord: Vector2i, alt: Dictionary
) -> Dictionary:
var alt_id := atlas.create_alternative_tile(coord)
var alt_td: TileData = atlas.get_tile_data(coord, alt_id)
if alt.has("flip_h"):
alt_td.flip_h = bool(alt["flip_h"])
if alt.has("flip_v"):
alt_td.flip_v = bool(alt["flip_v"])
if alt.has("transpose"):
alt_td.transpose = bool(alt["transpose"])
if alt.has("modulate"):
var m = alt["modulate"]
if typeof(m) == TYPE_DICTIONARY:
alt_td.modulate = Coerce.color_from_dict(m)
return {"alt_id": alt_id}