From dbe46a05bfeff8dffe140d24c6d9bc9b8911b852 Mon Sep 17 00:00:00 2001 From: iisaev Date: Sat, 24 Oct 2026 12:56:09 +0200 Subject: [PATCH] Event light, map ambient, DSL highlighting, autotile palette, action buttons on top --- locale/ui_en.properties | 8 ++++++ locale/ui_es.properties | 8 ++++++ locale/ui_ja.properties | 8 ++++++ locale/ui_ko.properties | 8 ++++++ locale/ui_ru.properties | 8 ++++++ locale/ui_vi.properties | 8 ++++++ locale/ui_zh.properties | 8 ++++++ src/RpgEntityPicker.cs | 18 +++++++----- src/RpgEventPropertiesPanel.cs | 18 +++++++++++- src/RpgMapEditorCanvas.cs | 8 +++++- src/RpgMapPropertiesPanel.cs | 14 +++++++++- src/RpgTilePalette.cs | 51 ++++++++++++++++++++++++++++------ 12 files changed, 146 insertions(+), 19 deletions(-) diff --git a/locale/ui_en.properties b/locale/ui_en.properties index c333178..6e3ea51 100644 --- a/locale/ui_en.properties +++ b/locale/ui_en.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = Found: {0} ×{1} tools/rpg_editor/placed_item = “{0}” lies on the map: event {1} tools/rpg_editor/picker_no_look = No map picture found, a marker is shown tools/rpg_editor/problem_event_locked = Event id {0} is taken by an event of another file or mod — pick another one +tools/rpg_editor/section_light = Light +tools/rpg_editor/light_radius = Light radius +tools/rpg_editor/light_radius_tip = In cells; 0 means the event gives no light. Light shows at dusk and at night +tools/rpg_editor/light_color = Light colour +tools/rpg_editor/light_color_tip = #rrggbb or #rrggbbaa (alpha is the strength); empty keeps the warm default +tools/rpg_editor/ambient = Fixed ambient +tools/rpg_editor/ambient_tip = #rrggbb: the map is always lit like this (a room with a lamp); empty: by the time of day +tools/rpg_editor/autotile = autotile diff --git a/locale/ui_es.properties b/locale/ui_es.properties index 2604616..abba442 100644 --- a/locale/ui_es.properties +++ b/locale/ui_es.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = Encontrado: {0} ×{1} tools/rpg_editor/placed_item = «{0}» está en el mapa: evento {1} tools/rpg_editor/picker_no_look = No se encontró imagen para el mapa, se muestra un marcador tools/rpg_editor/problem_event_locked = El id de evento {0} ya lo usa un evento de otro archivo o mod: elige otro +tools/rpg_editor/section_light = Luz +tools/rpg_editor/light_radius = Radio de luz +tools/rpg_editor/light_radius_tip = En casillas; 0: el evento no da luz. La luz se ve al anochecer y de noche +tools/rpg_editor/light_color = Color de la luz +tools/rpg_editor/light_color_tip = #rrggbb o #rrggbbaa (alfa = intensidad); vacío: el cálido por defecto +tools/rpg_editor/ambient = Luz ambiental fija +tools/rpg_editor/ambient_tip = #rrggbb: el mapa siempre tiene esta luz (una sala con lámpara); vacío: según la hora del día +tools/rpg_editor/autotile = autotile diff --git a/locale/ui_ja.properties b/locale/ui_ja.properties index d573020..bd4d510 100644 --- a/locale/ui_ja.properties +++ b/locale/ui_ja.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = 見つけた:{0} ×{1} tools/rpg_editor/placed_item = 「{0}」をマップに置いた:イベント {1} tools/rpg_editor/picker_no_look = マップ用の画像が見つからないため、マーカーを表示します tools/rpg_editor/problem_event_locked = イベント id {0} は別のファイルか MOD のイベントが使っています——別の id にしてください +tools/rpg_editor/section_light = 光 +tools/rpg_editor/light_radius = 光の半径 +tools/rpg_editor/light_radius_tip = マス単位。0 ならイベントは光らない。光は夕方と夜に見える +tools/rpg_editor/light_color = 光の色 +tools/rpg_editor/light_color_tip = #rrggbb か #rrggbbaa(アルファが強さ)。空なら既定の暖色 +tools/rpg_editor/ambient = 固定の環境光 +tools/rpg_editor/ambient_tip = #rrggbb:マップは常にこの明るさ(ランプのある部屋)。空なら時刻に従う +tools/rpg_editor/autotile = オートタイル diff --git a/locale/ui_ko.properties b/locale/ui_ko.properties index 8a7f436..29d7e92 100644 --- a/locale/ui_ko.properties +++ b/locale/ui_ko.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = 발견: {0} ×{1} tools/rpg_editor/placed_item = "{0}"이(가) 맵에 놓였습니다: 이벤트 {1} tools/rpg_editor/picker_no_look = 맵용 그림을 찾지 못해 표식을 표시합니다 tools/rpg_editor/problem_event_locked = 이벤트 id {0}은(는) 다른 파일이나 모드의 이벤트가 쓰고 있습니다 — 다른 것을 고르세요 +tools/rpg_editor/section_light = 빛 +tools/rpg_editor/light_radius = 빛 반경 +tools/rpg_editor/light_radius_tip = 칸 단위, 0이면 이벤트가 빛나지 않음. 빛은 해질녘과 밤에 보임 +tools/rpg_editor/light_color = 빛 색 +tools/rpg_editor/light_color_tip = #rrggbb 또는 #rrggbbaa(알파가 세기), 비우면 기본 따뜻한 색 +tools/rpg_editor/ambient = 고정 환경광 +tools/rpg_editor/ambient_tip = #rrggbb: 맵이 항상 이 빛으로 비침(램프가 있는 방), 비우면 시간대에 따름 +tools/rpg_editor/autotile = 오토타일 diff --git a/locale/ui_ru.properties b/locale/ui_ru.properties index 66d8ce1..894f610 100644 --- a/locale/ui_ru.properties +++ b/locale/ui_ru.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = Найдено: {0} ×{1} tools/rpg_editor/placed_item = «{0}» лежит на карте: событие {1} tools/rpg_editor/picker_no_look = Картинка для карты не найдена, будет маркер tools/rpg_editor/problem_event_locked = Id события {0} уже занят событием из другого файла или мода — выберите другой +tools/rpg_editor/section_light = Свет +tools/rpg_editor/light_radius = Радиус света +tools/rpg_editor/light_radius_tip = В клетках; 0 — событие не светит. Свет виден в сумерках и ночью +tools/rpg_editor/light_color = Цвет света +tools/rpg_editor/light_color_tip = #rrggbb или #rrggbbaa (альфа — сила); пусто — тёплый по умолчанию +tools/rpg_editor/ambient = Своё освещение +tools/rpg_editor/ambient_tip = #rrggbb: карта всегда освещена так (комната с лампой); пусто — по времени суток +tools/rpg_editor/autotile = автотайл diff --git a/locale/ui_vi.properties b/locale/ui_vi.properties index 8b29ede..4f98d73 100644 --- a/locale/ui_vi.properties +++ b/locale/ui_vi.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = Tìm thấy: {0} ×{1} tools/rpg_editor/placed_item = "{0}" nằm trên bản đồ: sự kiện {1} tools/rpg_editor/picker_no_look = Không tìm thấy hình cho bản đồ, hiển thị một điểm đánh dấu tools/rpg_editor/problem_event_locked = Id sự kiện {0} đã được một sự kiện ở tệp hoặc mod khác dùng — hãy chọn id khác +tools/rpg_editor/section_light = Ánh sáng +tools/rpg_editor/light_radius = Bán kính sáng +tools/rpg_editor/light_radius_tip = Tính theo ô; 0 là sự kiện không phát sáng. Ánh sáng hiện lúc chạng vạng và ban đêm +tools/rpg_editor/light_color = Màu ánh sáng +tools/rpg_editor/light_color_tip = #rrggbb hoặc #rrggbbaa (alpha là cường độ); để trống dùng màu ấm mặc định +tools/rpg_editor/ambient = Ánh sáng nền cố định +tools/rpg_editor/ambient_tip = #rrggbb: bản đồ luôn sáng như vậy (phòng có đèn); để trống thì theo giờ trong ngày +tools/rpg_editor/autotile = ô tự động diff --git a/locale/ui_zh.properties b/locale/ui_zh.properties index 0161a6b..2cfb836 100644 --- a/locale/ui_zh.properties +++ b/locale/ui_zh.properties @@ -102,3 +102,11 @@ tools/rpg_editor/item_found_line = 找到:{0} ×{1} tools/rpg_editor/placed_item = "{0}"放在地图上:事件 {1} tools/rpg_editor/picker_no_look = 没有找到地图用的图片,显示为标记 tools/rpg_editor/problem_event_locked = 事件 id {0} 已被其他文件或模组的事件占用——请换一个 +tools/rpg_editor/section_light = 光照 +tools/rpg_editor/light_radius = 光照半径 +tools/rpg_editor/light_radius_tip = 以格为单位;0 表示事件不发光。光在黄昏和夜晚可见 +tools/rpg_editor/light_color = 光的颜色 +tools/rpg_editor/light_color_tip = #rrggbb 或 #rrggbbaa(alpha 为强度);留空为默认暖色 +tools/rpg_editor/ambient = 固定环境光 +tools/rpg_editor/ambient_tip = #rrggbb:地图始终以此光照(点灯的房间);留空则随时间变化 +tools/rpg_editor/autotile = 自动图块 diff --git a/src/RpgEntityPicker.cs b/src/RpgEntityPicker.cs index b759461..5562ff0 100644 --- a/src/RpgEntityPicker.cs +++ b/src/RpgEntityPicker.cs @@ -28,9 +28,19 @@ namespace Cthangover.Tools.RpgEditor private readonly HBoxContainer buttons; private List all = new(); - /// Builds the search field, the list, the option and button rows and the hint. + /// + /// Builds the option and button rows with their hint on top — the list grows with the + /// entries (AutoHeight), so buttons below it would scroll out of sight — then the + /// search field and the list. + /// public RpgEntityPicker(string searchKey, string hintKey) { + options = new HBoxContainer(); + AddChild(options); + buttons = new HBoxContainer(); + AddChild(buttons); + AddChild(ToolUi.Hint(ToolUi.Tr(hintKey))); + search = ToolUi.Text(ToolUi.Tr(searchKey), OnSearchChanged); AddChild(search); @@ -43,12 +53,6 @@ namespace Cthangover.Tools.RpgEditor }; list.ItemSelected += OnItemSelected; AddChild(list); - - options = new HBoxContainer(); - AddChild(options); - buttons = new HBoxContainer(); - AddChild(buttons); - AddChild(ToolUi.Hint(ToolUi.Tr(hintKey))); } /// Reads the entries; called by . diff --git a/src/RpgEventPropertiesPanel.cs b/src/RpgEventPropertiesPanel.cs index 68e12c4..abf5140 100644 --- a/src/RpgEventPropertiesPanel.cs +++ b/src/RpgEventPropertiesPanel.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Cthangover.Mods.Rpg; using Cthangover.Tools.Common; +using Cthangover.Tools.Services; using Godot; namespace Cthangover.Tools.RpgEditor @@ -10,7 +11,8 @@ namespace Cthangover.Tools.RpgEditor /// /// Properties of the selected map event: id, cell, look (charset path, tileset index /// or item picture), facing, trigger, solidity, what it runs (scenario path, - /// inline DSL lines, or a teleport) and the condition that shows it. + /// inline DSL lines with highlighting, or a teleport), the light it gives off (radius 0 = none) + /// and the condition that shows it. /// /// A teleport is on when its map or cell is filled in; clearing the teleport /// map and both coordinates turns it off again. Solidity has a third state, @@ -40,6 +42,8 @@ namespace Cthangover.Tools.RpgEditor private readonly SpinBox teleportX; private readonly SpinBox teleportY; private readonly OptionButton teleportDir; + private readonly SpinBox lightRadius; + private readonly LineEdit lightColor; private RpgMapEvent mapEvent; private bool suppress; private bool editing; @@ -78,10 +82,15 @@ namespace Cthangover.Tools.RpgEditor body.AddChild(ToolUi.Hint(ToolUi.Tr("tools/rpg_editor/commands"))); commandsEdit = ToolUi.Code(); commandsEdit.CustomMinimumSize = new Vector2(0, 90); + commandsEdit.SyntaxHighlighter = ScenarioSyntaxService.CreateHighlighter(); commandsEdit.SizeFlagsVertical = SizeFlags.Fill; commandsEdit.TextChanged += OnCommandsChanged; body.AddChild(commandsEdit); + body.AddChild(ToolUi.Section("tools/rpg_editor/section_light")); + lightRadius = ToolUi.Field(body, "tools/rpg_editor/light_radius", ToolUi.Number(0, 16, 0.5, OnNumberChanged), "tools/rpg_editor/light_radius_tip"); + lightColor = ToolUi.Field(body, "tools/rpg_editor/light_color", ToolUi.Text("#ffd8a0", OnTextChanged), "tools/rpg_editor/light_color_tip"); + body.AddChild(ToolUi.Section("tools/rpg_editor/section_teleport")); teleportMap = ToolUi.Field(body, "tools/rpg_editor/teleport_map", ToolUi.Text("", OnTextChanged), "tools/rpg_editor/teleport_map_tip"); teleportX = ToolUi.Field(body, "tools/rpg_editor/x", ToolUi.Number(-1, 255, 1, OnNumberChanged)); @@ -119,6 +128,8 @@ namespace Cthangover.Tools.RpgEditor teleportX.Value = value.Teleport?.X ?? -1; teleportY.Value = value.Teleport?.Y ?? -1; teleportDir.Select((int)(value.Teleport?.Dir ?? RpgDirection.Down)); + lightRadius.Value = value.Light?.Radius ?? 0; + lightColor.Text = value.Light?.Color ?? string.Empty; suppress = false; } @@ -183,6 +194,11 @@ namespace Cthangover.Tools.RpgEditor Dir = (RpgDirection)teleportDir.Selected, } : null; + + // A radius of 0 means no light; an empty colour keeps the warm default. + mapEvent.Light = lightRadius.Value > 0 + ? new RpgLight { Radius = (float)lightRadius.Value, Color = NullIfEmpty(lightColor.Text) ?? new RpgLight().Color } + : null; Changed?.Invoke(); } diff --git a/src/RpgMapEditorCanvas.cs b/src/RpgMapEditorCanvas.cs index 8df057f..db087c4 100644 --- a/src/RpgMapEditorCanvas.cs +++ b/src/RpgMapEditorCanvas.cs @@ -157,8 +157,14 @@ namespace Cthangover.Tools.RpgEditor for (var x = 0; x < grid.Width; x++) { var index = grid.GetTile(layer, x, y); - if (index >= 0) + if (index < 0) + continue; + // Autotiles are assembled from their neighbours exactly like in the game. + var autotile = Tileset.AutotileOf(index); + if (autotile == null) DrawTextureRectRegion(Atlas, CellRect(x, y), TileRegion(index), modulate); + else if (textures.Tileset(autotile.Autotile) is { } block) + RpgAutotiles.Draw(this, block, CellRect(x, y), RpgAutotiles.QuartersAt(grid, layer, x, y, index), Tileset.TileSize, modulate); } } } diff --git a/src/RpgMapPropertiesPanel.cs b/src/RpgMapPropertiesPanel.cs index 5363a6f..9d9cebd 100644 --- a/src/RpgMapPropertiesPanel.cs +++ b/src/RpgMapPropertiesPanel.cs @@ -10,7 +10,8 @@ namespace Cthangover.Tools.RpgEditor { /// /// The map's own properties in the right column: id (the scene name, read - /// only — it is the file name), display name key, tileset, size, zoom, spawn + /// only — it is the file name), display name key, tileset, size, zoom, fixed + /// ambient light (empty: the time of day lights the map), spawn /// and random encounters. The size is applied with a button, because a /// smaller size cuts cells off. Encounter groups are written as /// slime; wolf,slime — groups split by semicolons. @@ -27,6 +28,7 @@ namespace Cthangover.Tools.RpgEditor private readonly SpinBox widthSpin; private readonly SpinBox heightSpin; private readonly SpinBox zoomSpin; + private readonly LineEdit ambientEdit; private readonly SpinBox spawnX; private readonly SpinBox spawnY; private readonly OptionButton spawnDir; @@ -61,6 +63,7 @@ namespace Cthangover.Tools.RpgEditor heightSpin = ToolUi.Field(this, "tools/rpg_editor/height", ToolUi.Number(1, 256, 1, null)); AddChild(ToolUi.Button("tools/rpg_editor/resize", OnResizePressed)); zoomSpin = ToolUi.Field(this, "tools/rpg_editor/zoom", ToolUi.Number(1, 4, 1, OnZoomChanged), "tools/rpg_editor/zoom_tip"); + ambientEdit = ToolUi.Field(this, "tools/rpg_editor/ambient", ToolUi.Text("#e0d2b8", OnAmbientChanged), "tools/rpg_editor/ambient_tip"); AddChild(ToolUi.Section("tools/rpg_editor/section_spawn")); spawnX = ToolUi.Field(this, "tools/rpg_editor/x", ToolUi.Number(0, 255, 1, OnSpawnChanged)); @@ -94,6 +97,7 @@ namespace Cthangover.Tools.RpgEditor widthSpin.Value = value.Grid.Width; heightSpin.Value = value.Grid.Height; zoomSpin.Value = map.Zoom; + ambientEdit.Text = map.Ambient ?? string.Empty; spawnX.Value = map.Spawn.X; spawnY.Value = map.Spawn.Y; spawnDir.Select((int)map.Spawn.Dir); @@ -123,6 +127,14 @@ namespace Cthangover.Tools.RpgEditor Changed?.Invoke(); } + private void OnAmbientChanged(string text) + { + if (!Begin()) + return; + document.Map.Ambient = string.IsNullOrWhiteSpace(text) ? null : text.Trim(); + Changed?.Invoke(); + } + private void OnTilesetPicked(long index) { if (!Begin() || index < 0 || index >= tilesets.Count) diff --git a/src/RpgTilePalette.cs b/src/RpgTilePalette.cs index 723a5f1..a568c5f 100644 --- a/src/RpgTilePalette.cs +++ b/src/RpgTilePalette.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Cthangover.Core.UI.Theming; using Cthangover.Mods.Rpg; @@ -10,16 +11,20 @@ namespace Cthangover.Tools.RpgEditor /// Brush picker of the editor's left column. For a tile layer it shows the /// tileset atlas at one and a half times the tile size with the selected tile framed and the /// solid tiles marked by a red corner; a click picks a tile, the tooltip - /// names it (index, tag, solid). For the collision layer it shows the three - /// collision codes instead. + /// names it (index, tag, solid). The tileset's autotiles follow in a row under the + /// atlas, each shown by its block's preview tile (). For the + /// collision layer it shows the three collision codes instead. /// public partial class RpgTilePalette : Control { private const float Scale = 1.5f; private const float CodeHeight = 44f; + private const float AutotileGap = 8f; private static readonly char[] Codes = { RpgCollision.Block, RpgCollision.Open, RpgCollision.Auto }; + private readonly RpgTextures textures = new(); + private readonly List autotiles = new(); private RpgTileset tileset; private Texture2D atlas; private bool collisionMode; @@ -49,6 +54,9 @@ namespace Cthangover.Tools.RpgEditor { tileset = value; atlas = image; + autotiles.Clear(); + if (value != null) + autotiles.AddRange(value.Tiles.Where(t => !string.IsNullOrEmpty(t.Autotile))); Relayout(); } @@ -80,15 +88,23 @@ namespace Cthangover.Tools.RpgEditor var cell = tileset.TileSize * Scale; DrawTextureRect(atlas, new Rect2(Vector2.Zero, atlas.GetSize() * Scale), false); - foreach (var tile in tileset.Tiles.Where(t => t.Solid)) + foreach (var tile in tileset.Tiles.Where(t => t.Solid && string.IsNullOrEmpty(t.Autotile))) + DrawSolidMark((Vector2)RpgTileSetBuilder.AtlasCoords(tileset, tile.Index) * cell, cell); + for (var i = 0; i < autotiles.Count; i++) { - var position = (Vector2)RpgTileSetBuilder.AtlasCoords(tileset, tile.Index) * cell; - DrawColoredPolygon(new[] { position, position + new Vector2(cell * 0.25f, 0f), position + new Vector2(0f, cell * 0.25f) }, new Color(0.9f, 0.2f, 0.2f, 0.9f)); + if (textures.Tileset(autotiles[i].Autotile) is { } block) + DrawTextureRectRegion(block, new Rect2(Slot(i), new Vector2(cell, cell)), RpgAutotiles.PreviewRect(tileset.TileSize)); + if (autotiles[i].Solid) + DrawSolidMark(Slot(i), cell); } - var selected = (Vector2)RpgTileSetBuilder.AtlasCoords(tileset, SelectedTile) * cell; + var slot = autotiles.FindIndex(t => t.Index == SelectedTile); + var selected = slot >= 0 ? Slot(slot) : (Vector2)RpgTileSetBuilder.AtlasCoords(tileset, SelectedTile) * cell; DrawRect(new Rect2(selected, new Vector2(cell, cell)), UiTokens.AccentBright, false, 3f); } + private void DrawSolidMark(Vector2 position, float cell) + => DrawColoredPolygon(new[] { position, position + new Vector2(cell * 0.25f, 0f), position + new Vector2(0f, cell * 0.25f) }, new Color(0.9f, 0.2f, 0.2f, 0.9f)); + private void DrawCode(int i) { var rect = new Rect2(0f, i * (CodeHeight + 6f), Size.X, CodeHeight); @@ -110,7 +126,7 @@ namespace Cthangover.Tools.RpgEditor if (collisionMode) CustomMinimumSize = new Vector2(200f, Codes.Length * (CodeHeight + 6f)); else - CustomMinimumSize = atlas != null ? atlas.GetSize() * Scale : Vector2.Zero; + CustomMinimumSize = atlas != null && tileset != null ? new Vector2(atlas.GetWidth() * Scale, AutotileTop + AutotileRows * tileset.TileSize * Scale) : Vector2.Zero; TooltipText = string.Empty; QueueRedraw(); } @@ -137,7 +153,7 @@ namespace Cthangover.Tools.RpgEditor else if (tileset != null && atlas != null) { var index = IndexAt(button.Position); - if (index >= 0 && index < RpgTileSetBuilder.TileCount(tileset, atlas)) + if (index >= 0 && (index < RpgTileSetBuilder.TileCount(tileset, atlas) || tileset.AutotileOf(index) != null)) { SelectedTile = index; TileSelected?.Invoke(index); @@ -147,9 +163,25 @@ namespace Cthangover.Tools.RpgEditor AcceptEvent(); } + private float AutotileTop => atlas == null ? 0f : atlas.GetHeight() * Scale + (autotiles.Count > 0 ? AutotileGap : 0f); + + private int AutotileRows => (autotiles.Count + Math.Max(1, tileset.Columns) - 1) / Math.Max(1, tileset.Columns); + + private Vector2 Slot(int i) + { + var cell = tileset.TileSize * Scale; + var columns = Math.Max(1, tileset.Columns); + return new Vector2(i % columns * cell, AutotileTop + i / columns * cell); + } + private int IndexAt(Vector2 position) { var cell = tileset.TileSize * Scale; + if (autotiles.Count > 0 && position.Y >= AutotileTop) + { + var slot = (int)((position.Y - AutotileTop) / cell) * Math.Max(1, tileset.Columns) + (int)(position.X / cell); + return slot >= 0 && slot < autotiles.Count && position.X < tileset.Columns * cell ? autotiles[slot].Index : -1; + } var column = (int)(position.X / cell); var row = (int)(position.Y / cell); return column < 0 || column >= tileset.Columns || row < 0 ? -1 : row * tileset.Columns + column; @@ -159,7 +191,8 @@ namespace Cthangover.Tools.RpgEditor { var info = tileset.Tiles.FirstOrDefault(t => t.Index == index); var solid = info?.Solid == true ? " · " + TranslationServer.Translate("tools/rpg_editor/solid_tile") : string.Empty; - return $"#{index} {info?.Tag}{solid}"; + var auto = string.IsNullOrEmpty(info?.Autotile) ? string.Empty : " · " + TranslationServer.Translate("tools/rpg_editor/autotile"); + return $"#{index} {info?.Tag}{solid}{auto}"; } } }