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

59 lines
2.6 KiB
GDScript

@tool
extends RefCounted
## Shared TileSet support leaf: load, validate, save-and-reindex a TileSet
## resource, plus the layer-node version hint and the full-tile polygon every
## tileset.* command group relies on.
##
## Stateless — every function takes its inputs (file path, TileSet, tile size) as
## parameters and returns its result (a TileSet-or-error Variant, an empty-or-error
## Dictionary, a hint String, or a polygon). No cross-call state; the file is all
## static, consumed by the tileset command children via a `preload` alias.
const Modules := preload("res://addons/godot_mcp_toolkit/core/modules.gd")
const FileGuard = Modules.FileGuard
const Helpers = Modules.CommandHelpers
## Load and validate a TileSet from file_path. Returns TileSet or error Dictionary.
static func load_tileset(file_path: String) -> Variant:
var guard := FileGuard.resolve_safe(file_path)
if guard["error"] != null:
return MCPToolkitError.fail("PATH_DENIED", str(guard["reason"]))
if not FileAccess.file_exists(file_path):
return MCPToolkitError.fail("NOT_FOUND", "TileSet not found: %s" % file_path)
var ts = ResourceLoader.load(file_path, "", ResourceLoader.CACHE_MODE_IGNORE)
if ts == null or not (ts is TileSet):
return MCPToolkitError.fail("INVALID_CLASS",
"Resource at %s is not a TileSet" % file_path)
return ts
## Save a TileSet, reload cache, and index. Returns empty dict on success or error.
static func save_tileset(ts: TileSet, file_path: String) -> Dictionary:
var save_err := ResourceSaver.save(ts, file_path)
if save_err != OK:
return MCPToolkitError.fail("SAVE_FAILED",
"ResourceSaver.save returned %d (path=%s)" % [save_err, file_path])
ResourceLoader.load(file_path, "", ResourceLoader.CACHE_MODE_REPLACE)
await Helpers.ensure_file_indexed(file_path)
return {}
## Build the response-tail hint that names the node a TileSet attaches to —
## TileMapLayer on 4.3+, TileMap on 4.2 — wrapped in the caller's prefix/suffix.
static func layer_node_hint(prefix: String, suffix: String) -> String:
var ver := Modules.VersionUtils.get_engine_version_pair()
var has_tilemaplayer := Modules.VersionUtils.is_at_least(ver, "4.3")
var node_name := "TileMapLayer" if has_tilemaplayer else "TileMap"
return prefix + node_name + suffix
## The unit rectangle spanning one tile (±w/2, ±h/2) — the default collision/nav/
## occlusion shape and the "full"/"one_way" collision shorthand.
static func build_full_tile_polygon(tile_size: Vector2i) -> PackedVector2Array:
var hw := tile_size.x / 2.0
var hh := tile_size.y / 2.0
return PackedVector2Array([
Vector2(-hw, -hh), Vector2(hw, -hh),
Vector2(hw, hh), Vector2(-hw, hh)])