Finished first EA Version #1

Merged
Nicola merged 110 commits from dev into main 2026-05-19 20:01:13 +02:00
11 changed files with 184 additions and 105 deletions
Showing only changes of commit 3060d3d6f7 - Show all commits
Binary file not shown.
+3
View File
@@ -32,6 +32,9 @@ bg_color = Color(0, 0, 0, 0.7647059)
[node name="World" type="Node3D" parent="." unique_id=770208789] [node name="World" type="Node3D" parent="." unique_id=770208789]
script = ExtResource("1_kldst") script = ExtResource("1_kldst")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="World" unique_id=690768533]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 177.60146, 0)
[node name="SteamworksHandler" type="Node" parent="." unique_id=1183440473] [node name="SteamworksHandler" type="Node" parent="." unique_id=1183440473]
script = ExtResource("2_b2bpf") script = ExtResource("2_b2bpf")
+2 -2
View File
@@ -21,9 +21,9 @@ public partial class Camera3d : Camera3D
Vector3 direction = Vector3.Zero; Vector3 direction = Vector3.Zero;
if (Input.IsActionPressed("move_forward") && Position.Z > 0) direction += Transform.Basis.Z; if (Input.IsActionPressed("move_forward") && Position.Z > 0) direction += Transform.Basis.Z;
if (Input.IsActionPressed("move_backward") && Position.Z < layerSize * 4) direction -= Transform.Basis.Z; if (Input.IsActionPressed("move_backward") && Position.Z < layerSize * 6) direction -= Transform.Basis.Z;
if (Input.IsActionPressed("move_left") && Position.X > 0) direction -= Transform.Basis.X; if (Input.IsActionPressed("move_left") && Position.X > 0) direction -= Transform.Basis.X;
if (Input.IsActionPressed("move_right") && Position.X < layerSize * 4) direction += Transform.Basis.X; if (Input.IsActionPressed("move_right") && Position.X < layerSize * 6) direction += Transform.Basis.X;
if (direction != Vector3.Zero) if (direction != Vector3.Zero)
{ {
-47
View File
@@ -1,47 +0,0 @@
using Godot;
using System;
using System.Collections.Generic;
public class DecorationHandler
{
public static void Spawn(List<TileRenderData> tiles, Dictionary<string, MeshInstance3D> decorationMeshes)
{
foreach (var data in tiles)
{
Node3D tileNode = data.Tile.DecorationNode;
foreach (var placeholder in data.Placeholders)
{
string key = placeholder.name.ToLower();
var decoration = new MeshInstance3D();
if (decorationMeshes.ContainsKey(key))
{
decoration.Mesh = decorationMeshes[key].Mesh;
}
else
{
continue;
}
decoration.Position = placeholder.pos;
if (key == "light")
{
decoration.AddChild(new OmniLight3D()
{
OmniAttenuation = 2f,
LightColor = new Color("#eae7ad"),
ShadowEnabled = true,
LightEnergy = 5f,
LightIndirectEnergy = 1.5f,
Position = new Vector3(0.5f, 0, 0)
});
}
tileNode.AddChild(decoration);
}
}
}
}
-1
View File
@@ -1 +0,0 @@
uid://oe2d2ape41jj
+2 -2
View File
@@ -2,8 +2,8 @@ public partial class GameData
{ {
//Amount of layers generated //Amount of layers generated
public static int ruinSize = 10; public static int ruinSize = 10;
//Width+Height of layers (+1 for the border) //Width+Height of layers
public static int layerSize = 20 + 1; public static int layerSize = 20;
//Current layer the player wants to see //Current layer the player wants to see
public static int currentLayer = 0; public static int currentLayer = 0;
//The layer that is currently visible //The layer that is currently visible
+49 -17
View File
@@ -5,12 +5,14 @@ using System.Linq;
using static WFC; using static WFC;
public partial class Layer : Node3D public partial class Layer : Node3D
{ {
Random rand = new Random();
private Node3D decorationRoot; private Node3D decorationRoot;
public Tile[,] tiles; public Tile[,] tiles;
int layerSize; int layerSize;
Tile tile; Tile tile;
int level; int level;
bool updateFailed = false; bool updateFailed = false;
public bool hasContentGenerated = false;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
@@ -31,7 +33,7 @@ public partial class Layer : Node3D
{ {
foreach (var tile in tiles) foreach (var tile in tiles)
{ {
foreach (Node child in tile.DecorationNode.GetChildren()) foreach (Node child in tile.ContentNode.GetChildren())
{ {
child.QueueFree(); child.QueueFree();
} }
@@ -63,10 +65,10 @@ public partial class Layer : Node3D
float offsetZ; float offsetZ;
for (int x = 0; x < layerSize; x++) for (int x = 0; x < layerSize; x++)
{ {
offsetX = x * 4; offsetX = x * 6;
for (int y = 0; y < layerSize; y++) for (int y = 0; y < layerSize; y++)
{ {
offsetZ = y * 4; offsetZ = y * 6;
position = new Vector3(offsetX, offsetY, offsetZ); position = new Vector3(offsetX, offsetY, offsetZ);
tile = new Tile(); tile = new Tile();
tile.SetMeshes(tileMeshes); tile.SetMeshes(tileMeshes);
@@ -87,7 +89,7 @@ public partial class Layer : Node3D
}; };
decorationRoot.AddChild(node); decorationRoot.AddChild(node);
tile.DecorationNode = node; tile.ContentNode = node;
} }
} }
@@ -112,7 +114,47 @@ public partial class Layer : Node3D
{ {
var tile = tiles[x, y]; var tile = tiles[x, y];
string result = tile.Collapse("border"); 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") if (result == "ERR")
return false; return false;
@@ -135,15 +177,7 @@ public partial class Layer : Node3D
Vector2I position = GetSmallestPossibilities(); Vector2I position = GetSmallestPossibilities();
while (true) while (true)
{ {
string keyword; string keyword = tiles[position.X, position.Y].Collapse("");
if (position.X == 0 || position.X == layerSize - 1 || position.Y == 0 || position.Y == layerSize - 1)
{
keyword = tiles[position.X, position.Y].Collapse("border");
}
else
{
keyword = tiles[position.X, position.Y].Collapse("");
}
if (keyword == "ERR") return false; if (keyword == "ERR") return false;
if (keyword != "") if (keyword != "")
{ {
@@ -160,8 +194,6 @@ public partial class Layer : Node3D
if (safetyCounter == layerSize * layerSize) return false; if (safetyCounter == layerSize * layerSize) return false;
} }
if (updateFailed) return false; if (updateFailed) return false;
//Spawn is a tile border, redo world generation
if (tiles[1, 1].collapsedMesh == "border") return false;
//Player has over 80% of tiles available without destroying walls => Results in about 95% success rate //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 //Not necessarily needed but improves the overall generation percentage at a low resource cost
if (!WFC.IsMapConnected(tiles, 0.8f)) return false; if (!WFC.IsMapConnected(tiles, 0.8f)) return false;
@@ -186,7 +218,7 @@ public partial class Layer : Node3D
for (int i = 0; i < dirs.Length; i++) for (int i = 0; i < dirs.Length; i++)
{ {
Vector2I newPos = currentPos + offsets[i]; Vector2I newPos = currentPos + offsets[i];
if (!InBounds(newPos, layerSize, true)) continue; if (!InBounds(newPos, layerSize)) continue;
Tile neighborTile = tiles[newPos.X, newPos.Y]; Tile neighborTile = tiles[newPos.X, newPos.Y];
+3 -3
View File
@@ -3,10 +3,10 @@ using Godot;
public class Placeholder public class Placeholder
{ {
public string name; public string name;
public Vector3 pos; public Transform3D transform;
public Placeholder(string name, Vector3 pos){ public Placeholder(string name, Transform3D transform){
this.name = name.Split("_")[0]; this.name = name.Split("_")[0];
this.pos = pos; this.transform = transform;
} }
} }
+11 -25
View File
@@ -53,14 +53,12 @@ public class WFC
["corner_down_right"] = new() { Direction.Down, Direction.Right }, ["corner_down_right"] = new() { Direction.Down, Direction.Right },
["junction"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right }, ["junction"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right },
["gate"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right }, ["gate"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right }
["border"] = new() { }
}; };
public static Dictionary<string, float> weights = new() public static Dictionary<string, float> weights = new()
{ {
["junction"] = 5f, ["junction"] = 3f,
["t_up"] = 3f, ["t_up"] = 3f,
["t_down"] = 3f, ["t_down"] = 3f,
["t_left"] = 3f, ["t_left"] = 3f,
@@ -69,19 +67,17 @@ public class WFC
["straight_left_right"] = 2f, ["straight_left_right"] = 2f,
["straight_up_down"] = 2f, ["straight_up_down"] = 2f,
["corner_up_left"] = 0.5f, ["corner_up_left"] = 0.7f,
["corner_up_right"] = 0.5f, ["corner_up_right"] = 0.7f,
["corner_down_left"] = 0.5f, ["corner_down_left"] = 0.7f,
["corner_down_right"] = 0.5f, ["corner_down_right"] = 0.7f,
["end_up"] = 0.2f, ["end_up"] = 0.2f,
["end_down"] = 0.1f, ["end_down"] = 0.1f,
["end_left"] = 0.2f, ["end_left"] = 0.2f,
["end_right"] = 0.3f, ["end_right"] = 0.3f,
["gate"] = 0.1f, ["gate"] = 0.1f
["border"] = 0.0f
}; };
public static Direction Opposite(Direction dir) public static Direction Opposite(Direction dir)
@@ -169,7 +165,7 @@ public class WFC
{ {
var next = position + offsets[i]; var next = position + offsets[i];
if (!InBounds(next, layer.GetLength(0), false)) if (!InBounds(next, layer.GetLength(0)))
continue; continue;
if (CanWalk(layer, position, next, dirs[i])) if (CanWalk(layer, position, next, dirs[i]))
@@ -189,21 +185,11 @@ public class WFC
return result; return result;
} }
public static bool InBounds(Vector2I pos, int layerSize, bool includeBorder = true) public static bool InBounds(Vector2I pos, int layerSize)
{
if (includeBorder)
{
return pos.X >= 0 &&
pos.Y >= 0 &&
pos.X < layerSize &&
pos.Y < layerSize;
}
else
{ {
return pos.X > 0 && return pos.X > 0 &&
pos.Y > 0 && pos.Y > 0 &&
pos.X < layerSize - 1 && pos.X < layerSize &&
pos.Y < layerSize - 1; pos.Y < layerSize;
}
} }
} }
+62 -1
View File
@@ -10,7 +10,10 @@ public partial class Tile
Random rand = new Random(); Random rand = new Random();
public Vector3 Position; public Vector3 Position;
public Vector2I GridPosition; public Vector2I GridPosition;
public Node3D DecorationNode; public Node3D ContentNode;
public bool containsLight, containsDecoration, containsResource;
public void SetMeshes(Dictionary<string, MeshInstance3D> tileMeshes) public void SetMeshes(Dictionary<string, MeshInstance3D> tileMeshes)
{ {
@@ -71,4 +74,62 @@ public partial class Tile
collapsedMesh = null; collapsedMesh = null;
SetMeshes(tileMeshes); SetMeshes(tileMeshes);
} }
public void SpawnContent(Dictionary<string, MeshInstance3D> contentMeshes, Transform3D transform, List<Placeholder> placeholders)
{
foreach (Placeholder placeholder in placeholders)
{
if (containsLight && placeholder.name.ToLower() == "light") SpawnLight(contentMeshes["light"], placeholder, transform);
else if (containsResource && placeholder.name.ToLower() == "resource") SpawnResource(contentMeshes["resource"], placeholder, transform);
else if (containsDecoration) SpawnDecorations(contentMeshes, placeholder, transform);
}
}
private void SpawnLight(MeshInstance3D lightMesh, Placeholder placeholder, Transform3D transform)
{
Vector3 forward = (transform.Origin - placeholder.transform.Origin).Normalized();
MeshInstance3D light = new MeshInstance3D
{
Mesh = lightMesh.Mesh,
Position = placeholder.transform.Origin
};
light.AddChild(new OmniLight3D()
{
OmniAttenuation = 2f,
LightColor = new Color("#eae7ad"),
ShadowEnabled = true,
LightEnergy = 5f,
LightIndirectEnergy = 1.5f,
Position = new Vector3(0.5f, 0, 0)
});
ContentNode.AddChild(light);
light.LookAt(light.Position + forward, Vector3.Up);
}
private void SpawnResource(MeshInstance3D resourceMesh, Placeholder placeholder, Transform3D transform)
{
Vector3 forward = (transform.Origin - placeholder.transform.Origin).Normalized();
MeshInstance3D resource = new MeshInstance3D
{
Mesh = resourceMesh.Mesh,
Position = placeholder.transform.Origin
};
ContentNode.AddChild(resource);
resource.LookAt(resource.Position + forward, Vector3.Up);
}
private void SpawnDecorations(Dictionary<string, MeshInstance3D> contentMeshes, Placeholder placeholder, Transform3D transform)
{
foreach (string key in contentMeshes.Keys)
{
if (key.ToLower() != placeholder.name.ToLower()) continue;
MeshInstance3D decoration = new MeshInstance3D
{
Mesh = contentMeshes[key].Mesh,
Position = placeholder.transform.Origin
};
ContentNode.AddChild(decoration);
}
}
} }
+51 -6
View File
@@ -6,8 +6,9 @@ using static GameData;
public partial class World : Node3D public partial class World : Node3D
{ {
Random rand = new Random();
public Dictionary<string, MeshInstance3D> tileMeshes; public Dictionary<string, MeshInstance3D> tileMeshes;
public Dictionary<string, MeshInstance3D> decorationMeshes; public Dictionary<string, MeshInstance3D> contentMeshes;
public Dictionary<string, List<Placeholder>> tilePlaceholders; public Dictionary<string, List<Placeholder>> tilePlaceholders;
PackedScene layerPrefab = ResourceLoader.LoadLayerPrefab(); PackedScene layerPrefab = ResourceLoader.LoadLayerPrefab();
private Dictionary<string, MultiMeshInstance3D> multiMeshes = new(); private Dictionary<string, MultiMeshInstance3D> multiMeshes = new();
@@ -22,7 +23,7 @@ public partial class World : Node3D
WFC.FillAdjacencies(); WFC.FillAdjacencies();
tileMeshes = ResourceLoader.LoadTiles(); tileMeshes = ResourceLoader.LoadTiles();
decorationMeshes = ResourceLoader.LoadDecorations(); contentMeshes = ResourceLoader.LoadDecorations();
tilePlaceholders = new Dictionary<string, List<Placeholder>>(); tilePlaceholders = new Dictionary<string, List<Placeholder>>();
@@ -31,7 +32,7 @@ public partial class World : Node3D
tilePlaceholders[kvp.Key] = new List<Placeholder>(); tilePlaceholders[kvp.Key] = new List<Placeholder>();
foreach (MeshInstance3D child in kvp.Value.GetChildren()) foreach (MeshInstance3D child in kvp.Value.GetChildren())
{ {
tilePlaceholders[kvp.Key].Add(new Placeholder(child.Name, child.Transform.Origin)); tilePlaceholders[kvp.Key].Add(new Placeholder(child.Name, child.Transform));
} }
meshLibrary[kvp.Key] = kvp.Value.Mesh; meshLibrary[kvp.Key] = kvp.Value.Mesh;
kvp.Value.QueueFree(); kvp.Value.QueueFree();
@@ -86,8 +87,8 @@ public partial class World : Node3D
if (Input.IsActionJustPressed("spawn_robot")) if (Input.IsActionJustPressed("spawn_robot"))
{ {
Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>(); Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>();
robot.Position = map[0].tiles[1,1].Position; robot.Position = map[0].tiles[1, 1].Position;
robot.Position -= new Vector3(0,1.5f,0); robot.Position -= new Vector3(0, 1.5f, 0);
AddChild(robot); AddChild(robot);
} }
} }
@@ -124,12 +125,56 @@ public partial class World : Node3D
} }
} }
if (!layer.hasContentGenerated)
{
DistributeTileContent(layer);
layer.hasContentGenerated = true;
}
return result; return result;
} }
private void DistributeTileContent(Layer layer)
{
int currentDecoration = 0;
int currentLight = 0;
int currentResource = 0;
int posX, posY;
while(currentLight < layerSize * 2)
{
posX = rand.Next(layerSize);
posY = rand.Next(layerSize);
if(layer.tiles[posX, posY].containsLight) continue;
layer.tiles[posX, posY].containsLight = true;
currentLight++;
}
while(currentDecoration < layerSize)
{
posX = rand.Next(layerSize);
posY = rand.Next(layerSize);
if(layer.tiles[posX, posY].containsDecoration) continue;
layer.tiles[posX, posY].containsDecoration = true;
currentDecoration++;
}
while(currentResource < layerSize)
{
posX = rand.Next(layerSize);
posY = rand.Next(layerSize);
if(layer.tiles[posX, posY].containsResource) continue;
layer.tiles[posX, posY].containsResource = true;
currentResource++;
}
}
private void HandleRenderData(List<TileRenderData> renderData) private void HandleRenderData(List<TileRenderData> renderData)
{ {
multiMeshHandler.Build(renderData); multiMeshHandler.Build(renderData);
DecorationHandler.Spawn(renderData, decorationMeshes); foreach (TileRenderData data in renderData)
{
data.Tile.SpawnContent(contentMeshes, data.Transform, data.Placeholders);
}
} }
} }