222 lines
5.9 KiB
C#
222 lines
5.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Godot;
|
|
|
|
public class WFC
|
|
{
|
|
public static Dictionary<string, Dictionary<Direction, List<string>>> adjacency = new Dictionary<string, Dictionary<Direction, List<string>>>();
|
|
public static Random rand = new Random();
|
|
public enum Direction
|
|
{
|
|
Backward,
|
|
Forward,
|
|
Left,
|
|
Right,
|
|
None,
|
|
Up,
|
|
Down
|
|
}
|
|
|
|
public static readonly Vector2I[] offsets =
|
|
{
|
|
new Vector2I(0, -1),
|
|
new Vector2I(0, 1),
|
|
new Vector2I(-1, 0),
|
|
new Vector2I(1, 0)
|
|
};
|
|
|
|
public static readonly Direction[] dirs =
|
|
{
|
|
Direction.Backward,
|
|
Direction.Forward,
|
|
Direction.Left,
|
|
Direction.Right
|
|
};
|
|
|
|
public static Dictionary<string, HashSet<Direction>> tileConnections = new Dictionary<string, HashSet<Direction>>
|
|
{
|
|
["t_right"] = new() { Direction.Backward, Direction.Forward, Direction.Right, Direction.Up },
|
|
["t_left"] = new() { Direction.Backward, Direction.Forward, Direction.Left, Direction.Up },
|
|
["t_up"] = new() { Direction.Left, Direction.Right, Direction.Backward, Direction.Up },
|
|
["t_down"] = new() { Direction.Left, Direction.Right, Direction.Forward, Direction.Up },
|
|
|
|
["end_up"] = new() { Direction.Backward, Direction.Up },
|
|
["end_down"] = new() { Direction.Forward, Direction.Up },
|
|
["end_left"] = new() { Direction.Left, Direction.Up },
|
|
["end_right"] = new() { Direction.Right, Direction.Up },
|
|
|
|
["straight_left_right"] = new() { Direction.Left, Direction.Right, Direction.Up },
|
|
["straight_up_down"] = new() { Direction.Backward, Direction.Forward, Direction.Up },
|
|
|
|
["corner_up_left"] = new() { Direction.Backward, Direction.Left, Direction.Up },
|
|
["corner_up_right"] = new() { Direction.Backward, Direction.Right, Direction.Up },
|
|
["corner_down_left"] = new() { Direction.Forward, Direction.Left, Direction.Up },
|
|
["corner_down_right"] = new() { Direction.Forward, Direction.Right, Direction.Up },
|
|
|
|
["junction"] = new() { Direction.Backward, Direction.Forward, Direction.Left, Direction.Right, Direction.Up },
|
|
["gate"] = new() { Direction.Backward, Direction.Forward, Direction.Left, Direction.Right, Direction.Up, Direction.Down }
|
|
};
|
|
|
|
public static Dictionary<string, float> weights = new()
|
|
{
|
|
["junction"] = 3f,
|
|
["t_up"] = 3f,
|
|
["t_down"] = 3f,
|
|
["t_left"] = 3f,
|
|
["t_right"] = 3f,
|
|
|
|
["straight_left_right"] = 2f,
|
|
["straight_up_down"] = 2f,
|
|
|
|
["corner_up_left"] = 0.7f,
|
|
["corner_up_right"] = 0.7f,
|
|
["corner_down_left"] = 0.7f,
|
|
["corner_down_right"] = 0.7f,
|
|
|
|
["end_up"] = 0.2f,
|
|
["end_down"] = 0.1f,
|
|
["end_left"] = 0.2f,
|
|
["end_right"] = 0.3f,
|
|
|
|
["gate"] = 0.0f
|
|
};
|
|
|
|
public static Direction Opposite(Direction dir)
|
|
{
|
|
return dir switch
|
|
{
|
|
Direction.Backward => Direction.Forward,
|
|
Direction.Forward => Direction.Backward,
|
|
Direction.Left => Direction.Right,
|
|
Direction.Right => Direction.Left,
|
|
Direction.Up => Direction.Down,
|
|
Direction.Down => Direction.Up,
|
|
_ => dir
|
|
};
|
|
}
|
|
|
|
public static bool CanConnect(string a, string b, Direction direction, bool checkWalking)
|
|
{
|
|
var aDirs = tileConnections[a];
|
|
var bDirs = tileConnections[b];
|
|
|
|
bool aOpen = aDirs.Contains(direction);
|
|
bool bOpen = bDirs.Contains(Opposite(direction));
|
|
|
|
if (checkWalking) return aOpen && bOpen;
|
|
return aOpen == bOpen;
|
|
}
|
|
|
|
|
|
|
|
public static void FillAdjacencies()
|
|
{
|
|
foreach (var tile in tileConnections.Keys)
|
|
{
|
|
adjacency[tile] = new Dictionary<Direction, List<string>>();
|
|
|
|
foreach (Direction dir in Enum.GetValues(typeof(Direction)))
|
|
{
|
|
var valid = new List<string>();
|
|
|
|
foreach (var other in tileConnections.Keys)
|
|
{
|
|
if (CanConnect(tile, other, dir, false))
|
|
valid.Add(other);
|
|
}
|
|
|
|
adjacency[tile][dir] = valid;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool IsWalkable(Tile tile)
|
|
{
|
|
var dirs = tileConnections[tile.collapsedMesh];
|
|
return dirs.Count > 0 && !dirs.Contains(Direction.None);
|
|
}
|
|
|
|
public static bool CanWalk(Tile[,] layer, Vector2I from, Vector2I to, Direction dir)
|
|
{
|
|
var fromTile = layer[from.X, from.Y];
|
|
var toTile = layer[to.X, to.Y];
|
|
|
|
if (!IsWalkable(toTile))
|
|
return false;
|
|
|
|
return CanConnect(fromTile.collapsedMesh, toTile.collapsedMesh, dir, true);
|
|
}
|
|
|
|
public static bool IsMapConnected(Tile[,] layer, float accessibilityThreshhold)
|
|
{
|
|
bool result = false;
|
|
HashSet<Vector2I> visited = new HashSet<Vector2I>();
|
|
List<Vector2I> toCheck = new List<Vector2I>();
|
|
Vector2I position;
|
|
toCheck.Add(new Vector2I(1, 1));
|
|
|
|
int safetyCounter = 0;
|
|
while (true)
|
|
{
|
|
if (toCheck.Count <= 0) break;
|
|
int index = rand.Next(toCheck.Count);
|
|
position = toCheck[index];
|
|
toCheck[index] = toCheck[^1];
|
|
toCheck.RemoveAt(toCheck.Count - 1);
|
|
if (!visited.Add(position)) continue;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
var next = position + offsets[i];
|
|
|
|
if (!InBounds(next, layer.GetLength(0)))
|
|
continue;
|
|
|
|
if (CanWalk(layer, position, next, dirs[i]))
|
|
{
|
|
toCheck.Add(next);
|
|
}
|
|
}
|
|
safetyCounter++;
|
|
if (safetyCounter > layer.Length * 2) break;
|
|
if (visited.Count >= Math.Pow(layer.GetLength(0) - 1, 2) * accessibilityThreshhold)
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static bool InBounds(Vector2I pos, int layerSize)
|
|
{
|
|
return pos.X > 0 &&
|
|
pos.Y > 0 &&
|
|
pos.X < layerSize &&
|
|
pos.Y < layerSize;
|
|
}
|
|
|
|
public static List<string> GetBorderPossibilities(int x, int z)
|
|
{
|
|
bool left = x == 0;
|
|
bool right = x == GameData.layerSize - 1;
|
|
bool top = z == 0;
|
|
bool bottom = z == GameData.layerSize - 1;
|
|
|
|
// Corners
|
|
if (left && top) return new() { "corner_down_right" };
|
|
if (left && bottom) return new() { "corner_up_right" };
|
|
if (right && top) return new() { "corner_down_left" };
|
|
if (right && bottom) return new() { "corner_up_left" };
|
|
|
|
// Edges
|
|
if (top) return new() { "straight_left_right", "t_down" };
|
|
if (bottom) return new() { "straight_left_right", "t_up" };
|
|
if (left) return new() { "straight_up_down", "t_right" };
|
|
if (right) return new() { "straight_up_down", "t_left" };
|
|
|
|
return new();
|
|
}
|
|
}
|