using Godot; using System.Collections.Generic; using AceFieldNewHorizon.Scripts.Tiles; namespace AceFieldNewHorizon.Scripts.System; public partial class NaturalResourceGenerator : Node2D { [Export] public GridManager Grid { get; set; } [Export] public BuildingRegistry Registry { get; set; } [Export] public int MapWidth = 100; [Export] public int MapHeight = 100; [Export] public float StoneDensity = 0.1f; // 10% chance for stone [Export] public float IronDensity = 0.03f; // 3% chance for iron (within stone) [Export] public int MinStoneVeinSize = 1; [Export] public int MaxStoneVeinSize = 5; [Export] public int MinIronVeinSize = 1; [Export] public int MaxIronVeinSize = 3; [Export] public int Seed; private RandomNumberGenerator _rng; private readonly List _groundTiles = []; private readonly List _stoneTiles = []; private readonly List _ironTiles = []; public override void _Ready() { _rng = new RandomNumberGenerator(); _rng.Seed = (ulong)(Seed != 0 ? Seed : (int)GD.Randi()); GenerateTerrain(); PlaceResources(); } private void GenerateTerrain() { // First pass: Generate base ground tiles for (int x = 0; x < MapWidth; x++) { for (int y = 0; y < MapHeight; y++) { var cell = new Vector2I(x, y); _groundTiles.Add(cell); PlaceTile("ground", cell); } } } private void PlaceResources() { // Create a copy of ground tiles for iteration var groundTilesToProcess = new List(_groundTiles); // Place stone veins foreach (var cell in groundTilesToProcess) { if (_rng.Randf() < StoneDensity) { var veinSize = _rng.RandiRange(MinStoneVeinSize, MaxStoneVeinSize); PlaceVein(cell, "stone", veinSize, _stoneTiles); } } // Create a copy of stone tiles for iteration var stoneTilesToProcess = new List(_stoneTiles); // Place iron veins within stone foreach (var stoneCell in stoneTilesToProcess) { if (_rng.Randf() < IronDensity) { var veinSize = _rng.RandiRange(MinIronVeinSize, MaxIronVeinSize); PlaceVein(stoneCell, "stone_iron", veinSize, _ironTiles); } } } private void PlaceVein(Vector2I startCell, string tileType, int maxVeinSize, ICollection tileList) { var queue = new Queue(); var placed = new HashSet(); queue.Enqueue(startCell); int placedCount = 0; while (queue.Count > 0 && placedCount < maxVeinSize) { var cell = queue.Dequeue(); if (placed.Contains(cell)) continue; if (!IsInBounds(cell)) continue; switch (tileType) { // For iron, make sure we're placing on stone case "stone_iron" when !_stoneTiles.Contains(cell): continue; // Remove from previous layer if needed case "stone_iron" when _stoneTiles.Contains(cell): _stoneTiles.Remove(cell); break; case "stone" when _groundTiles.Contains(cell): _groundTiles.Remove(cell); break; } PlaceTile(tileType, cell); tileList.Add(cell); placed.Add(cell); placedCount++; // Add adjacent cells to queue for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; // Skip self var neighbor = new Vector2I(cell.X + dx, cell.Y + dy); if (!placed.Contains(neighbor) && IsInBounds(neighbor)) { queue.Enqueue(neighbor); } } } } } private bool IsInBounds(Vector2I cell) { return cell.X >= 0 && cell.X < MapWidth && cell.Y >= 0 && cell.Y < MapHeight; } private void PlaceTile(string tileType, Vector2I cell) { var building = Registry.GetBuilding(tileType); if (building == null) return; var scene = building.Scene; var instance = (BaseTile)scene.Instantiate(); // Match PlacementManager's positioning logic var rotatedSize = building.GetRotatedSize(0f); var offset = GridUtils.GetCenterOffset(rotatedSize, 0f); instance.Position = GridUtils.GridToWorld(cell) + offset; instance.ZIndex = (int)building.Layer; AddChild(instance); // Make sure to use the building's size from the registry Grid.OccupyArea(cell, instance, building.Size, 0f, building.Layer); } }