#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 _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