114 lines
4.5 KiB
GDScript
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}
|