Restructered project folders
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
|
||||
public partial class GameData
|
||||
@@ -8,6 +9,9 @@ public partial class GameData
|
||||
public static int visibleLayer = 0;
|
||||
//Determines if the player can move the camera or not (Necessary for input and options menu)
|
||||
public static bool canMove = true;
|
||||
public static int maxRobotCount = 1000;
|
||||
public static List<Robot> robots = new List<Robot>();
|
||||
|
||||
//--- PLAYER ADJUSTABLE VALUES ---
|
||||
//Color used in primary objects (e.g. Robots)
|
||||
public static Color primaryColor = new Color("#276ac2");
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static WFC;
|
||||
public partial class Layer : Node3D
|
||||
{
|
||||
Random rand = new Random();
|
||||
private Node3D decorationRoot;
|
||||
public Tile[,] tiles;
|
||||
int layerSize;
|
||||
Tile tile;
|
||||
int level;
|
||||
bool updateFailed = false;
|
||||
public bool hasContentGenerated = false;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
decorationRoot = new Node3D
|
||||
{
|
||||
Name = "Decorations"
|
||||
};
|
||||
AddChild(decorationRoot);
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
}
|
||||
|
||||
public void ClearDecorations()
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
foreach (Node child in tile.ContentNode.GetChildren())
|
||||
{
|
||||
child.QueueFree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetupLayer(int layerSize, int level, Dictionary<string, MeshInstance3D> tileMeshes)
|
||||
{
|
||||
this.layerSize = layerSize;
|
||||
this.level = level;
|
||||
tiles = new Tile[layerSize, layerSize];
|
||||
GenerateBaseStructure(tileMeshes);
|
||||
int safetyCounter = 0;
|
||||
while (true)
|
||||
{
|
||||
if (GenerateLayer()) break;
|
||||
ResetLayer(tileMeshes);
|
||||
safetyCounter++;
|
||||
if (safetyCounter > 1000) break;
|
||||
}
|
||||
CreateTileNodes();
|
||||
}
|
||||
|
||||
private void GenerateBaseStructure(Dictionary<string, MeshInstance3D> tileMeshes)
|
||||
{
|
||||
Vector3 position;
|
||||
float offsetX;
|
||||
float offsetY = level * 4 * -1;
|
||||
float offsetZ;
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
offsetX = x * 6;
|
||||
for (int y = 0; y < layerSize; y++)
|
||||
{
|
||||
offsetZ = y * 6;
|
||||
position = new Vector3(offsetX, offsetY, offsetZ);
|
||||
tile = new Tile();
|
||||
tile.SetMeshes(tileMeshes);
|
||||
tile.Position = position;
|
||||
tile.GridPosition = new Vector2I(x, y);
|
||||
tiles[x, y] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTileNodes()
|
||||
{
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
var node = new Node3D
|
||||
{
|
||||
Position = tile.Position
|
||||
};
|
||||
decorationRoot.AddChild(node);
|
||||
|
||||
tile.ContentNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetLayer(Dictionary<string, MeshInstance3D> tileMeshes)
|
||||
{
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
for (int y = 0; y < layerSize; y++)
|
||||
{
|
||||
tiles[x, y].Reset(tileMeshes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool GenerateBorder()
|
||||
{
|
||||
for (int x = 0; x < layerSize; x++)
|
||||
{
|
||||
for (int y = 0; y < layerSize; y++)
|
||||
{
|
||||
if (x == 0 || y == 0 || x == layerSize - 1 || y == layerSize - 1)
|
||||
{
|
||||
var tile = tiles[x, y];
|
||||
|
||||
List<string> possibilities = new();
|
||||
|
||||
if(x == 0 && y == 0)
|
||||
{
|
||||
possibilities.Add("corner_down_right");
|
||||
}
|
||||
else if(x == 0 && y == layerSize - 1)
|
||||
{
|
||||
possibilities.Add("corner_up_right");
|
||||
}
|
||||
else if(x == layerSize - 1 && y == 0)
|
||||
{
|
||||
possibilities.Add("corner_down_left");
|
||||
}
|
||||
else if(x == layerSize - 1 && y == layerSize - 1)
|
||||
{
|
||||
possibilities.Add("corner_up_left");
|
||||
}
|
||||
else if(y == 0)
|
||||
{
|
||||
possibilities.Add("straight_left_right");
|
||||
possibilities.Add("t_down");
|
||||
}
|
||||
else if(y == layerSize - 1)
|
||||
{
|
||||
possibilities.Add("straight_left_right");
|
||||
possibilities.Add("t_up");
|
||||
}
|
||||
else if(x == 0)
|
||||
{
|
||||
possibilities.Add("straight_up_down");
|
||||
possibilities.Add("t_right");
|
||||
}
|
||||
else if(x == layerSize - 1)
|
||||
{
|
||||
possibilities.Add("straight_up_down");
|
||||
possibilities.Add("t_left");
|
||||
}
|
||||
|
||||
string result = tile.Collapse(possibilities[rand.Next(0, possibilities.Count)]);
|
||||
|
||||
if (result == "ERR")
|
||||
return false;
|
||||
|
||||
NewPropagate(new Vector2I(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GenerateLayer()
|
||||
{
|
||||
bool result = true;
|
||||
int safetyCounter = 0;
|
||||
if (!GenerateBorder())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2I position = GetSmallestPossibilities();
|
||||
while (true)
|
||||
{
|
||||
string keyword = tiles[position.X, position.Y].Collapse("");
|
||||
if (keyword == "ERR") return false;
|
||||
if (keyword != "")
|
||||
{
|
||||
NewPropagate(position);
|
||||
if (updateFailed) break;
|
||||
position = GetSmallestPossibilities();
|
||||
if (position == new Vector2(-100, -100))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
safetyCounter++;
|
||||
if (safetyCounter == layerSize * layerSize) return false;
|
||||
}
|
||||
if (updateFailed) return false;
|
||||
//Player has over 80% of tiles available without destroying walls => Results in about 95% success rate
|
||||
//Not necessarily needed but improves the overall generation percentage at a low resource cost
|
||||
if (!WFC.IsMapConnected(tiles, 0.8f)) return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void NewPropagate(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];
|
||||
|
||||
// Use CURRENT state of tile
|
||||
var currentPossibilities = currentTile.collapsedMesh != null
|
||||
? new HashSet<string> { currentTile.collapsedMesh }
|
||||
: new HashSet<string>(currentTile.tileMeshes.Keys);
|
||||
|
||||
for (int i = 0; i < dirs.Length; i++)
|
||||
{
|
||||
Vector2I newPos = currentPos + offsets[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;
|
||||
}
|
||||
|
||||
// ONLY enqueue if something changed
|
||||
if (updateCount > 0)
|
||||
{
|
||||
queue.Enqueue(newPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Vector2I GetSmallestPossibilities()
|
||||
{
|
||||
Vector2I result = new Vector2I(-100, -100);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://dkg0vq75koeig
|
||||
@@ -0,0 +1,28 @@
|
||||
using Godot;
|
||||
using GodotSteam;
|
||||
public partial class SteamworksHandler : Node
|
||||
{
|
||||
public override void _Ready()
|
||||
{
|
||||
return;
|
||||
SteamInitExStatus status = Steam.SteamInitEx(false).Status;
|
||||
if (status != 0)
|
||||
{
|
||||
GD.Print("Steam not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
GD.Print("Steam initialized!");
|
||||
GD.Print("User: " + Steam.GetPersonaName());
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
Steam.RunCallbacks();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
Steam.SteamShutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dqrdb3bvws6b6
|
||||
@@ -1,195 +0,0 @@
|
||||
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
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
None
|
||||
}
|
||||
|
||||
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.Up,
|
||||
Direction.Down,
|
||||
Direction.Left,
|
||||
Direction.Right
|
||||
};
|
||||
|
||||
public static Dictionary<string, HashSet<Direction>> tileConnections = new Dictionary<string, HashSet<Direction>>
|
||||
{
|
||||
["t_right"] = new() { Direction.Up, Direction.Down, Direction.Right },
|
||||
["t_left"] = new() { Direction.Up, Direction.Down, Direction.Left },
|
||||
["t_up"] = new() { Direction.Left, Direction.Right, Direction.Up },
|
||||
["t_down"] = new() { Direction.Left, Direction.Right, Direction.Down },
|
||||
|
||||
["end_up"] = new() { Direction.Up },
|
||||
["end_down"] = new() { Direction.Down },
|
||||
["end_left"] = new() { Direction.Left },
|
||||
["end_right"] = new() { Direction.Right },
|
||||
|
||||
["straight_left_right"] = new() { Direction.Left, Direction.Right },
|
||||
["straight_up_down"] = new() { Direction.Up, Direction.Down },
|
||||
|
||||
["corner_up_left"] = new() { Direction.Up, Direction.Left },
|
||||
["corner_up_right"] = new() { Direction.Up, Direction.Right },
|
||||
["corner_down_left"] = new() { Direction.Down, Direction.Left },
|
||||
["corner_down_right"] = new() { Direction.Down, Direction.Right },
|
||||
|
||||
["junction"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right },
|
||||
["gate"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right }
|
||||
};
|
||||
|
||||
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.1f
|
||||
};
|
||||
|
||||
public static Direction Opposite(Direction dir)
|
||||
{
|
||||
return dir switch
|
||||
{
|
||||
Direction.Up => Direction.Down,
|
||||
Direction.Down => Direction.Up,
|
||||
Direction.Left => Direction.Right,
|
||||
Direction.Right => Direction.Left,
|
||||
_ => 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;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://d3jw4gk5f8hhg
|
||||
Reference in New Issue
Block a user