371 lines
9.3 KiB
C#
371 lines
9.3 KiB
C#
using Godot;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using static GameData;
|
|
|
|
public partial class World : Node3D
|
|
{
|
|
public Dictionary<string, MeshInstance3D> tileMeshes;
|
|
public Dictionary<string, MeshInstance3D> contentMeshes;
|
|
public Dictionary<string, List<Placeholder>> tilePlaceholders;
|
|
private PackedScene layerPrefab = ResourceLoader.LoadLayerPrefab();
|
|
private Dictionary<string, MultiMeshInstance3D> multiMeshes = new Dictionary<string, MultiMeshInstance3D>();
|
|
private Dictionary<string, Mesh> meshLibrary = new Dictionary<string, Mesh>();
|
|
|
|
private MultiMeshHandler multiMeshHandler;
|
|
|
|
public override void _Ready()
|
|
{
|
|
bool shouldLoadSave = loadSaveOnStart && SaveGameManager.SaveExists();
|
|
SaveGameData saveGame = shouldLoadSave ? SaveGameManager.LoadSaveData() : null;
|
|
if (saveGame != null)
|
|
{
|
|
seed = saveGame.Seed;
|
|
}
|
|
|
|
ResetRunState();
|
|
WFC.FillAdjacencies();
|
|
|
|
tileMeshes = ResourceLoader.LoadTiles();
|
|
contentMeshes = ResourceLoader.LoadDecorations();
|
|
|
|
tilePlaceholders = new Dictionary<string, List<Placeholder>>();
|
|
|
|
foreach (KeyValuePair<string, MeshInstance3D> kvp in tileMeshes)
|
|
{
|
|
tilePlaceholders[kvp.Key] = new List<Placeholder>();
|
|
|
|
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();
|
|
SetGateRequirements();
|
|
|
|
if (shouldLoadSave && saveGame != null)
|
|
{
|
|
SaveGameManager.ApplyWorldData(saveGame);
|
|
}
|
|
|
|
Pathfinding.BuildAStarGraph();
|
|
|
|
HandleRenderData(BuildRenderData(visibleLayer));
|
|
|
|
if (shouldLoadSave && saveGame != null)
|
|
{
|
|
SpawnSavedRobots(saveGame.Robots);
|
|
}
|
|
else
|
|
{
|
|
SpawnDefaultRobot();
|
|
}
|
|
|
|
loadSaveOnStart = false;
|
|
}
|
|
|
|
private Dictionary<string, MultiMeshInstance3D> CreateMultiMeshes(Dictionary<string, Mesh> meshLibrary)
|
|
{
|
|
Dictionary<string, MultiMeshInstance3D> result = new Dictionary<string, MultiMeshInstance3D>();
|
|
|
|
foreach (KeyValuePair<string, Mesh> 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)
|
|
{
|
|
UpdateGameLoop(delta);
|
|
}
|
|
|
|
public void UpdateGameLoop(double delta)
|
|
{
|
|
if (isPaused) return;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
public void RefreshVisibleLayer()
|
|
{
|
|
ShowLayer(visibleLayer);
|
|
}
|
|
|
|
public void OpenGate(int layerIndex)
|
|
{
|
|
if (layerIndex < 0 || layerIndex >= map.Length) return;
|
|
|
|
Layer layer = map[layerIndex];
|
|
layer.OpenGate();
|
|
Pathfinding.UpdateGatePoint(layerIndex, true);
|
|
|
|
if (layerIndex == visibleLayer)
|
|
{
|
|
RefreshVisibleLayer();
|
|
}
|
|
}
|
|
|
|
private void SpawnDefaultRobot()
|
|
{
|
|
Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>();
|
|
robot.Name = "Robot #1";
|
|
robot.Position = map[0].tiles[0, 0].Position;
|
|
AddChild(robot);
|
|
robots.Add(robot);
|
|
}
|
|
|
|
private void SpawnSavedRobots(List<RobotSaveData> savedRobots)
|
|
{
|
|
if (savedRobots == null || savedRobots.Count <= 0)
|
|
{
|
|
SpawnDefaultRobot();
|
|
return;
|
|
}
|
|
|
|
foreach (RobotSaveData savedRobot in savedRobots)
|
|
{
|
|
Robot robot = ResourceLoader.LoadRobotPrefab().Instantiate<Robot>();
|
|
robot.LoadSaveData(savedRobot);
|
|
AddChild(robot);
|
|
robots.Add(robot);
|
|
}
|
|
}
|
|
|
|
private void GenerateWorld()
|
|
{
|
|
for (int layer = 0; layer < ruinSize; layer++)
|
|
{
|
|
Layer layerNode = layerPrefab.Instantiate<Layer>();
|
|
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<TileRenderData> BuildRenderData(int layerIndex)
|
|
{
|
|
List<TileRenderData> result = new List<TileRenderData>();
|
|
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];
|
|
if (layer.isGateOpen && tile.collapsedMesh == "gate")
|
|
{
|
|
continue;
|
|
}
|
|
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<TileRenderData> renderData)
|
|
{
|
|
multiMeshHandler.Build(renderData);
|
|
foreach (TileRenderData data in renderData)
|
|
{
|
|
data.Tile.SpawnContent(contentMeshes, data.Transform, data.Placeholders);
|
|
}
|
|
}
|
|
|
|
private void SetGateRequirements()
|
|
{
|
|
List<string> availableResources = new List<string>();
|
|
List<ItemData> possibleIngredients;
|
|
bool canCraft;
|
|
double highestCraftTime;
|
|
double lowestCraftTime;
|
|
foreach (Layer layer in map)
|
|
{
|
|
highestCraftTime = 0;
|
|
lowestCraftTime = double.MaxValue;
|
|
possibleIngredients = new List<ItemData>();
|
|
//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 = Mathf.Clamp(1 + layer.level / 3, 1, 4);
|
|
float craftTimeModifier = 0f;
|
|
double craftTimeLower, craftTimeUpper;
|
|
for (int i = 0; i < ingredientAmount; i++)
|
|
{
|
|
craftTimeLower = goalCraftTime - goalCraftTime * craftTimeModifier;
|
|
craftTimeUpper = goalCraftTime + goalCraftTime * craftTimeModifier;
|
|
|
|
List<ItemData> 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(3 + layer.level * 2, 9 + layer.level * 4)
|
|
});
|
|
craftTimeModifier = 0f;
|
|
}
|
|
}
|
|
}
|
|
}
|