protected virtual IEnumerator GenerateMainPath() { ChangeStatus(GenerationStatus.MainPath); nextNodeIndex = 0; List <GraphNode> handledNodes = new List <GraphNode>(DungeonFlow.Nodes.Count); bool isDone = false; int i = 0; // Keep track of these now, we'll need them later when we know the actual length of the dungeon List <List <TileSet> > tileSets = new List <List <TileSet> >(targetLength); List <DungeonArchetype> archetypes = new List <DungeonArchetype>(targetLength); List <GraphNode> nodes = new List <GraphNode>(targetLength); List <GraphLine> lines = new List <GraphLine>(targetLength); // We can't rigidly stick to the target length since we need at least one room for each node and that might be more than targetLength while (!isDone) { float depth = Mathf.Clamp(i / (float)(targetLength - 1), 0, 1); GraphLine lineSegment = DungeonFlow.GetLineAtDepth(depth); // This should never happen if (lineSegment == null) { yield return(Wait(InnerGenerate(true))); yield break; } // We're on a new line segment, change the current archetype if (lineSegment != previousLineSegment) { currentArchetype = lineSegment.DungeonArchetypes[RandomStream.Next(0, lineSegment.DungeonArchetypes.Count)]; previousLineSegment = lineSegment; } List <TileSet> useableTileSets = null; GraphNode nextNode = null; var orderedNodes = DungeonFlow.Nodes.OrderBy(x => x.Position).ToArray(); // Determine which node comes next foreach (var node in orderedNodes) { if (depth >= node.Position && !handledNodes.Contains(node)) { nextNode = node; handledNodes.Add(node); break; } } // Assign the TileSets to use based on whether we're on a node or a line segment if (nextNode != null) { useableTileSets = nextNode.TileSets; nextNodeIndex = (nextNodeIndex >= orderedNodes.Length - 1) ? -1 : nextNodeIndex + 1; archetypes.Add(null); lines.Add(null); nodes.Add(nextNode); if (nextNode == orderedNodes[orderedNodes.Length - 1]) { isDone = true; } } else { useableTileSets = currentArchetype.TileSets; archetypes.Add(currentArchetype); lines.Add(lineSegment); nodes.Add(null); } tileSets.Add(useableTileSets); i++; } int tileRetryCount = 0; int totalForLoopRetryCount = 0; for (int j = 0; j < tileSets.Count; j++) { var attachTo = (j == 0) ? null : currentDungeon.MainPathTiles[currentDungeon.MainPathTiles.Count - 1]; var tile = AddTile(attachTo, tileSets[j], j / (float)(tileSets.Count - 1), archetypes[j]); // if no tile could be generated delete last successful tile and retry from previous index // else return false if (j > 5 && tile == null && tileRetryCount < 5 && totalForLoopRetryCount < 20) { Tile previousTile = currentDungeon.MainPathTiles[j - 1]; foreach (var doorway in previousTile.Placement.AllDoorways) { allDoorways.Remove(doorway); } // If the tile we're removing was placed by tile injection, be sure to place the injected tile back on the pending list InjectedTile previousInjectedTile; if (injectedTiles.TryGetValue(previousTile, out previousInjectedTile)) { tilesPendingInjection.Add(previousInjectedTile); injectedTiles.Remove(previousTile); } currentDungeon.RemoveLastConnection(); currentDungeon.RemoveTile(previousTile); UnityUtil.Destroy(previousTile.gameObject); j -= 2; // -2 because loop adds 1 tileRetryCount++; totalForLoopRetryCount++; } else if (tile == null) { yield return(Wait(InnerGenerate(true))); yield break; } else { tile.Node = nodes[j]; tile.Line = lines[j]; tileRetryCount = 0; // Wait for a frame to allow for animated loading screens, etc if (ShouldSkipFrame(true)) { yield return(GetRoomPause()); } } } yield break; // Required for generation to run synchronously }