Files
przygody-pana-cegly/addons/ca_level_generator/CaLevelGeneratorDock.cs

361 lines
15 KiB
C#

#if TOOLS
using Godot;
namespace Mr.BrickAdventures.Tools.CaLevelGenerator;
[Tool]
public partial class CaLevelGeneratorDock : Control
{
public CaLevelGeneratorPlugin Plugin { get; set; }
// Controls referenced in Task 8 wiring
private OptionButton _layerDropdown;
private OptionButton _terrainDropdown;
private HBoxContainer _rawTileRow;
private SpinBox _sourceIdSpin;
private SpinBox _atlasXSpin;
private SpinBox _atlasYSpin;
private Button _caveBtn;
private Button _platformBtn;
private Button _terrainBtn;
private SpinBox _widthSpin;
private SpinBox _heightSpin;
private SpinBox _offsetXSpin;
private SpinBox _offsetYSpin;
private HSlider _densitySlider;
private Label _densityLabel;
private HSlider _smoothPassSlider;
private Label _smoothPassLabel;
private CheckBox _borderCheck;
private SpinBox _seedSpin;
private Button _genBtn;
private Button _smoothBtn;
private Button _clearBtn;
private CaMode _mode = CaMode.Cave;
private System.Collections.Generic.List<TileMapLayer> _layers = new();
private TileMapLayer _selectedLayer;
public override void _Ready()
{
CustomMinimumSize = new Vector2(200, 0);
var scroll = new ScrollContainer();
scroll.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
scroll.SizeFlagsVertical = SizeFlags.ExpandFill;
AddChild(scroll);
var vbox = new VBoxContainer();
vbox.SizeFlagsHorizontal = SizeFlags.ExpandFill;
scroll.AddChild(vbox);
// Target Layer
vbox.AddChild(new Label { Text = "Target Layer" });
_layerDropdown = new OptionButton();
_layerDropdown.SizeFlagsHorizontal = SizeFlags.ExpandFill;
vbox.AddChild(_layerDropdown);
// Terrain
vbox.AddChild(new Label { Text = "Terrain" });
_terrainDropdown = new OptionButton();
_terrainDropdown.SizeFlagsHorizontal = SizeFlags.ExpandFill;
vbox.AddChild(_terrainDropdown);
// Raw tile row (hidden when terrain is available)
_rawTileRow = new HBoxContainer();
_rawTileRow.Visible = false;
_rawTileRow.AddChild(new Label { Text = "Src" });
_sourceIdSpin = new SpinBox { MinValue = 0, MaxValue = 99, Value = 0 };
_rawTileRow.AddChild(_sourceIdSpin);
_rawTileRow.AddChild(new Label { Text = "X" });
_atlasXSpin = new SpinBox { MinValue = 0, MaxValue = 99, Value = 0 };
_rawTileRow.AddChild(_atlasXSpin);
_rawTileRow.AddChild(new Label { Text = "Y" });
_atlasYSpin = new SpinBox { MinValue = 0, MaxValue = 99, Value = 0 };
_rawTileRow.AddChild(_atlasYSpin);
vbox.AddChild(_rawTileRow);
// Mode toggle
vbox.AddChild(new Label { Text = "Mode" });
var modeRow = new HBoxContainer();
modeRow.SizeFlagsHorizontal = SizeFlags.ExpandFill;
_caveBtn = MakeModeButton("Cave", () => SetMode(CaMode.Cave));
_platformBtn = MakeModeButton("Platform", () => SetMode(CaMode.Platform));
_terrainBtn = MakeModeButton("Terrain", () => SetMode(CaMode.Terrain));
_caveBtn.ButtonPressed = true;
modeRow.AddChild(_caveBtn);
modeRow.AddChild(_platformBtn);
modeRow.AddChild(_terrainBtn);
vbox.AddChild(modeRow);
vbox.AddChild(new HSeparator());
// Width / Height
var dimRow = new HBoxContainer();
dimRow.AddChild(MakeSpinColumn("Width", out _widthSpin, 5, 500, 40));
dimRow.AddChild(MakeSpinColumn("Height", out _heightSpin, 5, 500, 22));
vbox.AddChild(dimRow);
// Offset X / Y
var offRow = new HBoxContainer();
offRow.AddChild(MakeSpinColumn("Offset X", out _offsetXSpin, -9999, 9999, 0));
offRow.AddChild(MakeSpinColumn("Offset Y", out _offsetYSpin, -9999, 9999, 0));
vbox.AddChild(offRow);
// Fill Density
var densHeader = new HBoxContainer();
densHeader.AddChild(new Label { Text = "Fill Density", SizeFlagsHorizontal = SizeFlags.ExpandFill });
_densityLabel = new Label { Text = "48%" };
densHeader.AddChild(_densityLabel);
vbox.AddChild(densHeader);
_densitySlider = new HSlider { MinValue = 0, MaxValue = 100, Value = 48, Step = 1 };
_densitySlider.ValueChanged += v => _densityLabel.Text = $"{(int)v}%";
vbox.AddChild(_densitySlider);
// Smoothing Passes
var smoothHeader = new HBoxContainer();
smoothHeader.AddChild(new Label { Text = "Smoothing Passes", SizeFlagsHorizontal = SizeFlags.ExpandFill });
_smoothPassLabel = new Label { Text = "4" };
smoothHeader.AddChild(_smoothPassLabel);
vbox.AddChild(smoothHeader);
_smoothPassSlider = new HSlider { MinValue = 0, MaxValue = 10, Value = 4, Step = 1 };
_smoothPassSlider.ValueChanged += v => _smoothPassLabel.Text = $"{(int)v}";
vbox.AddChild(_smoothPassSlider);
// Border walls
_borderCheck = new CheckBox { Text = "Solid border walls", ButtonPressed = true };
vbox.AddChild(_borderCheck);
// Seed
vbox.AddChild(new Label { Text = "Seed" });
var seedRow = new HBoxContainer();
_seedSpin = new SpinBox { MinValue = 0, MaxValue = int.MaxValue, Value = 12345,
AllowGreater = true, SizeFlagsHorizontal = SizeFlags.ExpandFill };
var randBtn = new Button { Text = "🎲" };
randBtn.Pressed += () => _seedSpin.Value = GD.Randi();
seedRow.AddChild(_seedSpin);
seedRow.AddChild(randBtn);
vbox.AddChild(seedRow);
vbox.AddChild(new HSeparator());
// Action buttons
_genBtn = new Button { Text = "▶ Generate", SizeFlagsHorizontal = SizeFlags.ExpandFill };
vbox.AddChild(_genBtn);
var actionRow = new HBoxContainer();
actionRow.SizeFlagsHorizontal = SizeFlags.ExpandFill;
_smoothBtn = new Button { Text = "✦ Smooth", SizeFlagsHorizontal = SizeFlags.ExpandFill };
_clearBtn = new Button { Text = "✕ Clear", SizeFlagsHorizontal = SizeFlags.ExpandFill };
actionRow.AddChild(_smoothBtn);
actionRow.AddChild(_clearBtn);
vbox.AddChild(actionRow);
_layerDropdown.ItemSelected += OnLayerSelected;
_terrainDropdown.ItemSelected += _ => SaveSettings();
_genBtn.Pressed += OnGenerate;
_smoothBtn.Pressed += OnSmooth;
_clearBtn.Pressed += OnClear;
LoadSettings();
}
public void OnSceneChanged(Node sceneRoot)
{
ScanLayers(sceneRoot);
RestoreLastLayer(sceneRoot?.SceneFilePath ?? "");
}
private void SetMode(CaMode mode)
{
_mode = mode;
_caveBtn.ButtonPressed = mode == CaMode.Cave;
_platformBtn.ButtonPressed = mode == CaMode.Platform;
_terrainBtn.ButtonPressed = mode == CaMode.Terrain;
}
private static Button MakeModeButton(string text, System.Action onPressed)
{
var btn = new Button { Text = text, ToggleMode = true,
SizeFlagsHorizontal = SizeFlags.ExpandFill };
btn.Pressed += onPressed;
return btn;
}
private static Control MakeSpinColumn(string label, out SpinBox spin,
double min, double max, double value)
{
var col = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
col.AddChild(new Label { Text = label });
spin = new SpinBox { MinValue = min, MaxValue = max, Value = value,
SizeFlagsHorizontal = SizeFlags.ExpandFill };
col.AddChild(spin);
return col;
}
private void ScanLayers(Node root)
{
_layers.Clear();
_layerDropdown.Clear();
if (root == null) return;
CollectLayers(root);
foreach (var layer in _layers)
_layerDropdown.AddItem(layer.Name);
if (_layers.Count > 0) OnLayerSelected(0);
}
private void CollectLayers(Node node)
{
if (node is TileMapLayer layer) _layers.Add(layer);
foreach (Node child in node.GetChildren()) CollectLayers(child);
}
private void OnLayerSelected(long index)
{
if (index < 0 || index >= _layers.Count) return;
_selectedLayer = _layers[(int)index];
PopulateTerrainDropdown(_selectedLayer);
var sceneRoot = EditorInterface.Singleton.GetEditedSceneRoot();
if (sceneRoot != null)
{
var key = $"ca_generator/last_layer_{sceneRoot.SceneFilePath}";
EditorInterface.Singleton.GetEditorSettings()
.SetSetting(key, _selectedLayer.GetPath().ToString());
}
SaveSettings();
}
private void RestoreLastLayer(string sceneFilePath)
{
if (string.IsNullOrEmpty(sceneFilePath) || _layers.Count == 0) return;
var key = $"ca_generator/last_layer_{sceneFilePath}";
var es = EditorInterface.Singleton.GetEditorSettings();
if (!es.HasSetting(key)) return;
var path = es.GetSetting(key).AsString();
for (int i = 0; i < _layers.Count; i++)
{
if (_layers[i].GetPath().ToString() == path)
{
_layerDropdown.Select(i);
OnLayerSelected(i);
return;
}
}
}
private void PopulateTerrainDropdown(TileMapLayer layer)
{
_terrainDropdown.Clear();
var tileSet = layer.TileSet;
bool hasTerrains = false;
if (tileSet != null)
{
for (int ts = 0; ts < tileSet.GetTerrainSetsCount(); ts++)
{
for (int t = 0; t < tileSet.GetTerrainsCount(ts); t++)
{
_terrainDropdown.AddItem(tileSet.GetTerrainName(ts, t));
_terrainDropdown.SetItemMetadata(
_terrainDropdown.ItemCount - 1,
Variant.From((ts << 16) | t));
hasTerrains = true;
}
}
}
_terrainDropdown.Visible = hasTerrains;
_rawTileRow.Visible = !hasTerrains;
}
private CaGeneratorSettings BuildSettings()
{
int terrainSet = -1, terrain = 0;
if (_terrainDropdown.Visible && _terrainDropdown.ItemCount > 0)
{
int packed = _terrainDropdown.GetSelectedMetadata().AsInt32();
terrainSet = packed >> 16;
terrain = packed & 0xFFFF;
}
return new CaGeneratorSettings
{
Mode = _mode,
Width = (int)_widthSpin.Value,
Height = (int)_heightSpin.Value,
Offset = new Vector2I((int)_offsetXSpin.Value, (int)_offsetYSpin.Value),
FillDensity = (float)_densitySlider.Value / 100f,
SmoothingPasses = (int)_smoothPassSlider.Value,
BorderWalls = _borderCheck.ButtonPressed,
Seed = (int)_seedSpin.Value,
TerrainSet = terrainSet,
Terrain = terrain,
SourceId = (int)_sourceIdSpin.Value,
AtlasCoords = new Vector2I((int)_atlasXSpin.Value, (int)_atlasYSpin.Value),
};
}
private void OnGenerate()
{
if (_selectedLayer == null) return;
var settings = BuildSettings();
var grid = CaGenerator.Generate(settings);
TilemapPainter.Paint(_selectedLayer, grid, settings, Plugin.UndoRedo);
SaveSettings();
}
private void OnSmooth()
{
if (_selectedLayer == null) return;
TilemapPainter.Smooth(_selectedLayer, BuildSettings(), Plugin.UndoRedo);
}
private void OnClear()
{
if (_selectedLayer == null) return;
TilemapPainter.Clear(_selectedLayer, BuildSettings(), Plugin.UndoRedo);
}
private void SaveSettings()
{
var es = EditorInterface.Singleton.GetEditorSettings();
es.SetSetting("ca_generator/mode", (int)_mode);
es.SetSetting("ca_generator/width", (int)_widthSpin.Value);
es.SetSetting("ca_generator/height", (int)_heightSpin.Value);
es.SetSetting("ca_generator/offset_x", (int)_offsetXSpin.Value);
es.SetSetting("ca_generator/offset_y", (int)_offsetYSpin.Value);
es.SetSetting("ca_generator/fill_density", _densitySlider.Value);
es.SetSetting("ca_generator/smoothing_passes", (int)_smoothPassSlider.Value);
es.SetSetting("ca_generator/border_walls", _borderCheck.ButtonPressed);
es.SetSetting("ca_generator/seed", (int)_seedSpin.Value);
es.SetSetting("ca_generator/source_id", (int)_sourceIdSpin.Value);
es.SetSetting("ca_generator/atlas_x", (int)_atlasXSpin.Value);
es.SetSetting("ca_generator/atlas_y", (int)_atlasYSpin.Value);
}
private void LoadSettings()
{
var es = EditorInterface.Singleton.GetEditorSettings();
if (es == null) return;
SetMode((CaMode)(es.HasSetting("ca_generator/mode") ? es.GetSetting("ca_generator/mode").AsInt32() : 0));
_widthSpin.Value = es.HasSetting("ca_generator/width") ? es.GetSetting("ca_generator/width").AsDouble() : 40.0;
_heightSpin.Value = es.HasSetting("ca_generator/height") ? es.GetSetting("ca_generator/height").AsDouble() : 22.0;
_offsetXSpin.Value = es.HasSetting("ca_generator/offset_x") ? es.GetSetting("ca_generator/offset_x").AsDouble() : 0.0;
_offsetYSpin.Value = es.HasSetting("ca_generator/offset_y") ? es.GetSetting("ca_generator/offset_y").AsDouble() : 0.0;
_densitySlider.Value = es.HasSetting("ca_generator/fill_density") ? es.GetSetting("ca_generator/fill_density").AsDouble() : 48.0;
_smoothPassSlider.Value = es.HasSetting("ca_generator/smoothing_passes") ? es.GetSetting("ca_generator/smoothing_passes").AsDouble() : 4.0;
_borderCheck.ButtonPressed = es.HasSetting("ca_generator/border_walls") ? es.GetSetting("ca_generator/border_walls").AsBool() : true;
_seedSpin.Value = es.HasSetting("ca_generator/seed") ? es.GetSetting("ca_generator/seed").AsDouble() : 12345.0;
_sourceIdSpin.Value = es.HasSetting("ca_generator/source_id") ? es.GetSetting("ca_generator/source_id").AsDouble() : 0.0;
_atlasXSpin.Value = es.HasSetting("ca_generator/atlas_x") ? es.GetSetting("ca_generator/atlas_x").AsDouble() : 0.0;
_atlasYSpin.Value = es.HasSetting("ca_generator/atlas_y") ? es.GetSetting("ca_generator/atlas_y").AsDouble() : 0.0;
_densityLabel.Text = $"{(int)_densitySlider.Value}%";
_smoothPassLabel.Text = $"{(int)_smoothPassSlider.Value}";
}
}
#endif