diff --git a/Assets/Objects/Tiles.glb b/Assets/Objects/Tiles.glb index bd49be2..b1d54d4 100644 Binary files a/Assets/Objects/Tiles.glb and b/Assets/Objects/Tiles.glb differ diff --git a/Scenes/Game.tscn b/Scenes/Game.tscn index ba2e2d3..e1508e7 100644 --- a/Scenes/Game.tscn +++ b/Scenes/Game.tscn @@ -32,6 +32,9 @@ bg_color = Color(0, 0, 0, 0.7647059) [node name="World" type="Node3D" parent="." unique_id=770208789] 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] script = ExtResource("2_b2bpf") diff --git a/Scripts/Camera3d.cs b/Scripts/Camera3d.cs index 2b0ea57..c84f3b2 100644 --- a/Scripts/Camera3d.cs +++ b/Scripts/Camera3d.cs @@ -21,9 +21,9 @@ public partial class Camera3d : Camera3D Vector3 direction = Vector3.Zero; 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_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) { diff --git a/Scripts/Helpers/DecorationHandler.cs b/Scripts/Helpers/DecorationHandler.cs deleted file mode 100644 index 5f2e27f..0000000 --- a/Scripts/Helpers/DecorationHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Godot; -using System; -using System.Collections.Generic; - -public class DecorationHandler -{ - public static void Spawn(List tiles, Dictionary 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); - } - } - } -} \ No newline at end of file diff --git a/Scripts/Helpers/DecorationHandler.cs.uid b/Scripts/Helpers/DecorationHandler.cs.uid deleted file mode 100644 index dd2fb96..0000000 --- a/Scripts/Helpers/DecorationHandler.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://oe2d2ape41jj diff --git a/Scripts/Helpers/GameData.cs b/Scripts/Helpers/GameData.cs index e6f37d3..49bc73b 100644 --- a/Scripts/Helpers/GameData.cs +++ b/Scripts/Helpers/GameData.cs @@ -2,8 +2,8 @@ public partial class GameData { //Amount of layers generated public static int ruinSize = 10; - //Width+Height of layers (+1 for the border) - public static int layerSize = 20 + 1; + //Width+Height of layers + public static int layerSize = 20; //Current layer the player wants to see public static int currentLayer = 0; //The layer that is currently visible diff --git a/Scripts/Helpers/Layer.cs b/Scripts/Helpers/Layer.cs index 5cc6acf..6ea1132 100644 --- a/Scripts/Helpers/Layer.cs +++ b/Scripts/Helpers/Layer.cs @@ -5,12 +5,14 @@ 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() @@ -31,7 +33,7 @@ public partial class Layer : Node3D { foreach (var tile in tiles) { - foreach (Node child in tile.DecorationNode.GetChildren()) + foreach (Node child in tile.ContentNode.GetChildren()) { child.QueueFree(); } @@ -63,10 +65,10 @@ public partial class Layer : Node3D float offsetZ; for (int x = 0; x < layerSize; x++) { - offsetX = x * 4; + offsetX = x * 6; for (int y = 0; y < layerSize; y++) { - offsetZ = y * 4; + offsetZ = y * 6; position = new Vector3(offsetX, offsetY, offsetZ); tile = new Tile(); tile.SetMeshes(tileMeshes); @@ -87,7 +89,7 @@ public partial class Layer : Node3D }; decorationRoot.AddChild(node); - tile.DecorationNode = node; + tile.ContentNode = node; } } @@ -112,7 +114,47 @@ public partial class Layer : Node3D { var tile = tiles[x, y]; - string result = tile.Collapse("border"); + List 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; @@ -135,15 +177,7 @@ public partial class Layer : Node3D Vector2I position = GetSmallestPossibilities(); while (true) { - string keyword; - 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(""); - } + string keyword = tiles[position.X, position.Y].Collapse(""); if (keyword == "ERR") return false; if (keyword != "") { @@ -160,8 +194,6 @@ public partial class Layer : Node3D if (safetyCounter == layerSize * layerSize) 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 //Not necessarily needed but improves the overall generation percentage at a low resource cost 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++) { Vector2I newPos = currentPos + offsets[i]; - if (!InBounds(newPos, layerSize, true)) continue; + if (!InBounds(newPos, layerSize)) continue; Tile neighborTile = tiles[newPos.X, newPos.Y]; diff --git a/Scripts/Helpers/Placeholder.cs b/Scripts/Helpers/Placeholder.cs index 79031dd..db99f50 100644 --- a/Scripts/Helpers/Placeholder.cs +++ b/Scripts/Helpers/Placeholder.cs @@ -3,10 +3,10 @@ using Godot; public class Placeholder { 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.pos = pos; + this.transform = transform; } } \ No newline at end of file diff --git a/Scripts/Helpers/WFC.cs b/Scripts/Helpers/WFC.cs index 038a5f1..c564479 100644 --- a/Scripts/Helpers/WFC.cs +++ b/Scripts/Helpers/WFC.cs @@ -53,14 +53,12 @@ public class WFC ["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 }, - - ["border"] = new() { } + ["gate"] = new() { Direction.Up, Direction.Down, Direction.Left, Direction.Right } }; public static Dictionary weights = new() { - ["junction"] = 5f, + ["junction"] = 3f, ["t_up"] = 3f, ["t_down"] = 3f, ["t_left"] = 3f, @@ -69,19 +67,17 @@ public class WFC ["straight_left_right"] = 2f, ["straight_up_down"] = 2f, - ["corner_up_left"] = 0.5f, - ["corner_up_right"] = 0.5f, - ["corner_down_left"] = 0.5f, - ["corner_down_right"] = 0.5f, + ["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, - - ["border"] = 0.0f + ["gate"] = 0.1f }; public static Direction Opposite(Direction dir) @@ -169,7 +165,7 @@ public class WFC { var next = position + offsets[i]; - if (!InBounds(next, layer.GetLength(0), false)) + if (!InBounds(next, layer.GetLength(0))) continue; if (CanWalk(layer, position, next, dirs[i])) @@ -189,21 +185,11 @@ public class WFC 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 && + return pos.X > 0 && + pos.Y > 0 && pos.X < layerSize && pos.Y < layerSize; - } - else - { - return pos.X > 0 && - pos.Y > 0 && - pos.X < layerSize - 1 && - pos.Y < layerSize - 1; - } } } diff --git a/Scripts/Tile.cs b/Scripts/Tile.cs index 5f2df8f..f42a8e0 100644 --- a/Scripts/Tile.cs +++ b/Scripts/Tile.cs @@ -10,7 +10,10 @@ public partial class Tile Random rand = new Random(); public Vector3 Position; public Vector2I GridPosition; - public Node3D DecorationNode; + public Node3D ContentNode; + + public bool containsLight, containsDecoration, containsResource; + public void SetMeshes(Dictionary tileMeshes) { @@ -71,4 +74,62 @@ public partial class Tile collapsedMesh = null; SetMeshes(tileMeshes); } + + public void SpawnContent(Dictionary contentMeshes, Transform3D transform, List 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 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); + } + } } diff --git a/Scripts/World.cs b/Scripts/World.cs index 48a4f79..0d185ac 100644 --- a/Scripts/World.cs +++ b/Scripts/World.cs @@ -6,8 +6,9 @@ using static GameData; public partial class World : Node3D { + Random rand = new Random(); public Dictionary tileMeshes; - public Dictionary decorationMeshes; + public Dictionary contentMeshes; public Dictionary> tilePlaceholders; PackedScene layerPrefab = ResourceLoader.LoadLayerPrefab(); private Dictionary multiMeshes = new(); @@ -22,7 +23,7 @@ public partial class World : Node3D WFC.FillAdjacencies(); tileMeshes = ResourceLoader.LoadTiles(); - decorationMeshes = ResourceLoader.LoadDecorations(); + contentMeshes = ResourceLoader.LoadDecorations(); tilePlaceholders = new Dictionary>(); @@ -31,7 +32,7 @@ public partial class World : Node3D tilePlaceholders[kvp.Key] = new List(); 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; kvp.Value.QueueFree(); @@ -39,7 +40,7 @@ public partial class World : Node3D multiMeshes = CreateMultiMeshes(meshLibrary); multiMeshHandler = new MultiMeshHandler(multiMeshes); - + map = new Layer[ruinSize]; GenerateWorld(); @@ -86,8 +87,8 @@ public partial class World : Node3D if (Input.IsActionJustPressed("spawn_robot")) { Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate(); - robot.Position = map[0].tiles[1,1].Position; - robot.Position -= new Vector3(0,1.5f,0); + robot.Position = map[0].tiles[1, 1].Position; + robot.Position -= new Vector3(0, 1.5f, 0); AddChild(robot); } } @@ -124,12 +125,56 @@ public partial class World : Node3D } } + if (!layer.hasContentGenerated) + { + DistributeTileContent(layer); + layer.hasContentGenerated = true; + } 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 renderData) { multiMeshHandler.Build(renderData); - DecorationHandler.Spawn(renderData, decorationMeshes); + foreach (TileRenderData data in renderData) + { + data.Tile.SpawnContent(contentMeshes, data.Transform, data.Placeholders); + } } }