Files
AceField-New-Horizon/Scripts/System/NaturalResourceGenerator.cs
2025-08-28 01:12:00 +08:00

154 lines
4.1 KiB
C#

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<Vector2I> _groundTiles = [];
private readonly List<Vector2I> _stoneTiles = [];
private readonly List<Vector2I> _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<Vector2I>(_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<Vector2I>(_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<Vector2I> tileList)
{
var queue = new Queue<Vector2I>();
var placed = new HashSet<Vector2I>();
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);
}
}