Cleaned up project with better structure.
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
using static WFC;
|
||||
|
||||
public partial class Layer : Node3D
|
||||
{
|
||||
private const int MaxGenerationAttempts = 1000;
|
||||
private static readonly Vector2I NoPosition = new Vector2I(-100, -100);
|
||||
|
||||
private Node3D decorationRoot;
|
||||
public Tile[,] tiles;
|
||||
private int layerSize;
|
||||
private int level;
|
||||
private bool updateFailed = false;
|
||||
public bool hasContentGenerated = false;
|
||||
public Vector2I gateCoordinate;
|
||||
public List<string> currentResources;
|
||||
public bool isGateOpen = false;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
currentResources = new List<string>();
|
||||
decorationRoot = new Node3D
|
||||
{
|
||||
Name = "Decorations"
|
||||
};
|
||||
AddChild(decorationRoot);
|
||||
}
|
||||
|
||||
public void ClearDecorations()
|
||||
{
|
||||
foreach (Tile tile in tiles)
|
||||
{
|
||||
foreach (Node child in tile.ContentNode.GetChildren())
|
||||
{
|
||||
child.QueueFree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupLayer(int layerSize, int level, Dictionary<string, MeshInstance3D> tileMeshes, Vector2I collapseOrigin)
|
||||
{
|
||||
this.layerSize = layerSize;
|
||||
this.level = level;
|
||||
tiles = new Tile[layerSize, layerSize];
|
||||
|
||||
GenerateBaseStructure(tileMeshes);
|
||||
|
||||
int safetyCounter = 0;
|
||||
while (true)
|
||||
{
|
||||
if (GenerateLayer(collapseOrigin)) break;
|
||||
ResetLayer(tileMeshes);
|
||||
safetyCounter++;
|
||||
if (safetyCounter > MaxGenerationAttempts) break;
|
||||
}
|
||||
CreateTileNodes();
|
||||
}
|
||||
|
||||
private void GenerateBaseStructure(Dictionary<string, MeshInstance3D> tileMeshes)
|
||||
{
|
||||
Vector3 position;
|
||||
float offsetX;
|
||||
float offsetY = level * GameData.tileHeight * -1;
|
||||
float offsetZ;
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
offsetX = x * GameData.tileWidth;
|
||||
for (int y = 0; y < layerSize; y++)
|
||||
{
|
||||
offsetZ = y * GameData.tileWidth;
|
||||
position = new Vector3(offsetX, offsetY, offsetZ);
|
||||
Tile tile = new Tile();
|
||||
tile.SetMeshes(tileMeshes);
|
||||
tile.Position = position;
|
||||
tile.GridPosition = new Vector2I(x, y);
|
||||
tiles[x, y] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTileNodes()
|
||||
{
|
||||
foreach (Tile tile in tiles)
|
||||
{
|
||||
Node3D node = new Node3D
|
||||
{
|
||||
Position = tile.Position,
|
||||
Visible = tile.collapsedMesh != null && tile.collapsedMesh == "gate"
|
||||
};
|
||||
decorationRoot.AddChild(node);
|
||||
|
||||
tile.ContentNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetLayer(Dictionary<string, MeshInstance3D> tileMeshes)
|
||||
{
|
||||
updateFailed = false;
|
||||
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
for (int y = 0; y < layerSize; y++)
|
||||
{
|
||||
tiles[x, y].Reset(tileMeshes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateBorder()
|
||||
{
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
for (int z = 0; z < layerSize; z++)
|
||||
{
|
||||
if (x == 0 && z == 0 && level == 0) continue;
|
||||
if (!IsBorder(x, z))
|
||||
continue;
|
||||
|
||||
Tile tile = tiles[x, z];
|
||||
List<string> possibilities = GetBorderPossibilities(x, z);
|
||||
|
||||
if (possibilities.Count == 0)
|
||||
continue;
|
||||
|
||||
tile.Collapse(possibilities[GameData.rand.Next(possibilities.Count)]);
|
||||
Propagate(new Vector2I(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsBorder(int x, int z)
|
||||
{
|
||||
return x == 0 || z == 0 || x == layerSize - 1 || z == layerSize - 1;
|
||||
}
|
||||
|
||||
private void GenerateNecessaryTiles()
|
||||
{
|
||||
//Generate spawn only in the first layer
|
||||
if (level == 0)
|
||||
{
|
||||
tiles[0, 0].Collapse("spawn");
|
||||
Propagate(new Vector2I());
|
||||
}
|
||||
|
||||
int posX, posY;
|
||||
while (true)
|
||||
{
|
||||
posX = GameData.rand.Next(layerSize);
|
||||
posY = GameData.rand.Next(layerSize);
|
||||
|
||||
if (tiles[posX, posY].collapsedMesh != null) continue;
|
||||
if (tiles[posX, posY].tileMeshes.ContainsKey("gate"))
|
||||
{
|
||||
tiles[posX, posY].Collapse("gate");
|
||||
tiles[posX, posY].containsDecoration = true;
|
||||
gateCoordinate = new Vector2I(posX, posY);
|
||||
Propagate(gateCoordinate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool GenerateLayer(Vector2I collapseOrigin)
|
||||
{
|
||||
int safetyCounter = 0;
|
||||
GenerateBorder();
|
||||
|
||||
GenerateNecessaryTiles();
|
||||
Vector2I position = tiles[collapseOrigin.X, collapseOrigin.Y].collapsedMesh == null ? collapseOrigin : GetSmallestPossibilities();
|
||||
while (true)
|
||||
{
|
||||
string keyword = tiles[position.X, position.Y].Collapse("");
|
||||
if (keyword == "ERR") return false;
|
||||
if (keyword != "")
|
||||
{
|
||||
Propagate(position);
|
||||
if (updateFailed) break;
|
||||
position = GetSmallestPossibilities();
|
||||
if (position == NoPosition)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
safetyCounter++;
|
||||
if (safetyCounter == layerSize * layerSize) return false;
|
||||
}
|
||||
if (updateFailed) return false;
|
||||
if (!WFC.IsMapConnected(tiles, 0.8f)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Propagate(Vector2I startPos)
|
||||
{
|
||||
Queue<Vector2I> queue = new Queue<Vector2I>();
|
||||
queue.Enqueue(startPos);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
Vector2I currentPos = queue.Dequeue();
|
||||
Tile currentTile = tiles[currentPos.X, currentPos.Y];
|
||||
|
||||
HashSet<string> currentPossibilities = currentTile.collapsedMesh != null
|
||||
? new HashSet<string> { currentTile.collapsedMesh }
|
||||
: new HashSet<string>(currentTile.tileMeshes.Keys);
|
||||
|
||||
for (int i = 0; i < offsets2D.Length; i++)
|
||||
{
|
||||
Vector2I newPos = currentPos + offsets2D[i];
|
||||
if (!InBounds(newPos, layerSize)) continue;
|
||||
|
||||
Tile neighborTile = tiles[newPos.X, newPos.Y];
|
||||
|
||||
HashSet<string> allowed = new HashSet<string>();
|
||||
|
||||
foreach (string neighborOption in neighborTile.tileMeshes.Keys)
|
||||
{
|
||||
foreach (string item in currentPossibilities)
|
||||
{
|
||||
if (WFC.CanConnect(item, neighborOption, dirs[i], false))
|
||||
{
|
||||
allowed.Add(neighborOption);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int updateCount = neighborTile.Propagate(allowed);
|
||||
|
||||
if (updateCount == int.MaxValue)
|
||||
{
|
||||
updateFailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateCount > 0)
|
||||
{
|
||||
queue.Enqueue(newPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Vector2I GetSmallestPossibilities()
|
||||
{
|
||||
Vector2I result = NoPosition;
|
||||
int lowest = 100;
|
||||
int current;
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
for (int y = 0; y < layerSize; y++)
|
||||
{
|
||||
if (tiles[x, y].collapsedMesh != null) continue;
|
||||
current = tiles[x, y].tileMeshes.Count;
|
||||
if (current < lowest)
|
||||
{
|
||||
result = new Vector2I(x, y);
|
||||
lowest = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user