using Godot; using System.Collections.Generic; using System.Linq; using static GameData; public partial class World : Node3D { public Dictionary tileMeshes; public Dictionary contentMeshes; public Dictionary> tilePlaceholders; private PackedScene layerPrefab = ResourceLoader.LoadLayerPrefab(); private Dictionary multiMeshes = new Dictionary(); private Dictionary meshLibrary = new Dictionary(); private MultiMeshHandler multiMeshHandler; public override void _Ready() { ResetRunState(); WFC.FillAdjacencies(); tileMeshes = ResourceLoader.LoadTiles(); contentMeshes = ResourceLoader.LoadDecorations(); tilePlaceholders = new Dictionary>(); foreach (KeyValuePair kvp in tileMeshes) { tilePlaceholders[kvp.Key] = new List(); foreach (Node3D child in kvp.Value.GetChildren()) { tilePlaceholders[kvp.Key].Add(new Placeholder(child.Name, child.Transform)); } meshLibrary[kvp.Key] = kvp.Value.Mesh; kvp.Value.QueueFree(); } multiMeshes = CreateMultiMeshes(meshLibrary); multiMeshHandler = new MultiMeshHandler(multiMeshes); map = new Layer[ruinSize]; GenerateWorld(); Pathfinding.BuildAStarGraph(); HandleRenderData(BuildRenderData(0)); Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate(); robot.Name = "Bob"; robot.Position = map[0].tiles[0, 0].Position; AddChild(robot); robots.Add(robot); SetGateRequirements(); } private Dictionary CreateMultiMeshes(Dictionary meshLibrary) { Dictionary result = new Dictionary(); foreach (KeyValuePair kvp in meshLibrary) { MultiMesh multiMesh = new MultiMesh { Mesh = kvp.Value, TransformFormat = MultiMesh.TransformFormatEnum.Transform3D }; MultiMeshInstance3D instance = new MultiMeshInstance3D { Multimesh = multiMesh }; AddChild(instance); result[kvp.Key] = instance; } return result; } public override void _Process(double delta) { GameData.survival.Update(delta); if (!canMove) return; if (Input.IsActionJustPressed("layer_up") && currentLayer > 0) currentLayer--; if (Input.IsActionJustPressed("layer_down") && currentLayer < ruinSize - 1 && map[currentLayer].isGateOpen) currentLayer++; if (currentLayer != visibleLayer) { ShowLayer(currentLayer); } } private void ResetRunState() { survival = new SurvivalState(); robotStats = new RobotStats(); inventory = new Inventory(); availableResearch = ResourceLoader.LoadResearch(); robots.Clear(); currentLayer = 0; visibleLayer = 0; lowestLayer = 0; canMove = true; } private void GenerateWorld() { for (int layer = 0; layer < ruinSize; layer++) { Layer layerNode = layerPrefab.Instantiate(); AddChild(layerNode); if (layer == 0) { layerNode.SetupLayer(layerSize, layer, tileMeshes, new Vector2I()); } else { layerNode.SetupLayer(layerSize, layer, tileMeshes, map[layer - 1].gateCoordinate); } map[layer] = layerNode; } SetupSpawn(); } private void ShowLayer(int layer) { map[visibleLayer].ClearDecorations(); HandleRenderData(BuildRenderData(layer)); visibleLayer = layer; } private void SetupSpawn() { map[0].tiles[0, 0].wasVisited = true; map[0].tiles[0, 0].containsDecoration = true; map[0].tiles[0, 0].containsLight = true; map[0].tiles[0, 0].containsResource = false; map[0].tiles[0, 0].ContentNode.Visible = true; } private List BuildRenderData(int layerIndex) { List result = new List(); Layer layer = map[layerIndex]; layer.ClearDecorations(); for (int x = 0; x < layerSize; x++) { for (int y = 0; y < layerSize; y++) { Tile tile = layer.tiles[x, y]; result.Add(new TileRenderData { Tile = tile, MeshKey = tile.collapsedMesh, Transform = new Transform3D(Basis.Identity, tile.Position), Placeholders = tilePlaceholders[tile.collapsedMesh] }); } } 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 * 3) { posX = rand.Next(layerSize); posY = rand.Next(layerSize); if (CannotContainLight(layer.tiles[posX, posY])) continue; 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; layer.tiles[posX, posY].resource = new GameResource(ResourceDistributor.GetResource(layer.level)); layer.currentResources.Add(layer.tiles[posX, posY].resource.name); currentResource++; } } private bool CannotContainLight(Tile tile) { return tile.collapsedMesh == "junction" || tile.collapsedMesh == "gate"; } private void HandleRenderData(List renderData) { multiMeshHandler.Build(renderData); foreach (TileRenderData data in renderData) { data.Tile.SpawnContent(contentMeshes, data.Transform, data.Placeholders); } } private void SetGateRequirements() { List availableResources = new List(); List possibleIngredients; bool canCraft; double highestCraftTime; double lowestCraftTime; foreach (Layer layer in map) { highestCraftTime = 0; lowestCraftTime = double.MaxValue; possibleIngredients = new List(); //Step 1: Determine all possible resources for this and all previous layers combined foreach (string resource in layer.currentResources) { if (availableResources.Contains(resource)) continue; availableResources.Add(resource); } //Step 2: Check which items can be crafted with those items, repeat until no further items are added to the list bool addedNewItem; do { addedNewItem = false; foreach (ItemData item in availableItems.Values) { if (possibleIngredients.Any(existing => existing.Id == item.Id)) continue; canCraft = item.Inputs.All(input => availableResources.Contains(input.Item)); if (!canCraft) continue; possibleIngredients.Add(item); availableResources.Add(item.Id); lowestCraftTime = Mathf.Min(lowestCraftTime, item.CraftTime); highestCraftTime = Mathf.Max(highestCraftTime, item.CraftTime); addedNewItem = true; } } while (addedNewItem); //Step 3: Choose gate items needed based on crafting time and layer it is for (Lower layers -> More advanced items -> More crafting time) double goalCraftTime = Mathf.Lerp(lowestCraftTime, highestCraftTime, Mathf.Clamp(layer.level/(float)ruinSize, 0, 1)); int ingredientAmount = rand.Next(1, 1 + ruinSize / 2); float craftTimeModifier = 0f; double craftTimeLower, craftTimeUpper; for (int i = 0; i < ingredientAmount; i++) { craftTimeLower = goalCraftTime - goalCraftTime * craftTimeModifier; craftTimeUpper = goalCraftTime + goalCraftTime * craftTimeModifier; List validIngredients = possibleIngredients .Where(item => item.CraftTime >= craftTimeLower && item.CraftTime <= craftTimeUpper && !layer.gateIngredients.Any(ingredient => ingredient.Item == item.Id)) .ToList(); if (validIngredients.Count == 0) { i--; craftTimeModifier += 0.05f; continue; } ItemData item = validIngredients[rand.Next(validIngredients.Count)]; layer.gateIngredients.Add(new Ingredient { Item = item.Id, Amount = rand.Next(5 + layer.level, 20 + layer.level) }); craftTimeModifier = 0f; } } } }