Exemple #1
0
        protected virtual void LockDoorway(Doorway doorway, Key key, KeyManager keyManager)
        {
            var placement = doorway.Tile.Placement;
            var prefabs   = doorway.Tile.TileSet.LockPrefabs.Where(x => { return(DoorwaySocket.IsMatchingSocket(x.SocketGroup, doorway.SocketGroup)); }).Select(x => x.LockPrefabs).ToArray();

            if (prefabs.Length == 0)
            {
                return;
            }

            var chosenEntry = prefabs[RandomStream.Next(0, prefabs.Length)].GetRandom(RandomStream, placement.IsOnMainPath, placement.NormalizedDepth, null, true);
            var prefab      = chosenEntry.Value;

            GameObject doorObj = GameObject.Instantiate(prefab);

            doorObj.transform.parent   = Root.transform;
            doorObj.transform.position = doorway.transform.position;
            doorObj.transform.rotation = doorway.transform.rotation;

            // Set this locked door as the current door prefab
            doorway.SetUsedPrefab(doorObj);
            doorway.ConnectedDoorway.SetUsedPrefab(doorObj);

            DungeonUtil.AddAndSetupDoorComponent(CurrentDungeon, doorObj, doorway);

            foreach (var keylock in doorObj.GetComponentsInChildren <Component>().OfType <IKeyLock>())
            {
                keylock.OnKeyAssigned(key, keyManager);
            }
        }
Exemple #2
0
        protected PreProcessTileData PickRandomTemplate(DoorwaySocketType?socketGroupFilter)
        {
            // Pick a random tile
            var tile     = useableTiles[RandomStream.Next(0, useableTiles.Count)];
            var template = GetTileTemplate(tile);

            // If there's a socket group filter and the chosen Tile doesn't have a socket of this type, try again
            if (socketGroupFilter.HasValue && !template.DoorwaySockets.Contains(socketGroupFilter.Value))
            {
                return(PickRandomTemplate(socketGroupFilter));
            }

            return(template);
        }
Exemple #3
0
        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
        }
Exemple #4
0
        protected virtual IEnumerator InnerGenerate(bool isRetry)
        {
            if (isRetry)
            {
                ChosenSeed   = RandomStream.Next();
                RandomStream = new Random(ChosenSeed);


                if (retryCount >= MaxAttemptCount && Application.isEditor)
                {
                    string errorText = "Failed to generate the dungeon " + MaxAttemptCount + " times.\n" +
                                       "This could indicate a problem with the way the tiles are set up. Try to make sure most rooms have more than one doorway and that all doorways are easily accessible.\n" +
                                       "Here are a list of all reasons a tile placement had to be retried:";

                    foreach (var pair in tilePlacementResultCounters)
                    {
                        if (pair.Value > 0)
                        {
                            errorText += "\n" + pair.Key + " (x" + pair.Value + ")";
                        }
                    }

                    Debug.LogError(errorText);
                    ChangeStatus(GenerationStatus.Failed);
                    yield break;
                }

                retryCount++;
                GenerationStats.IncrementRetryCount();

                if (Retrying != null)
                {
                    Retrying();
                }
            }
            else
            {
                retryCount = 0;
                GenerationStats.Clear();
            }

            currentDungeon = Root.GetComponent <Dungeon>();
            if (currentDungeon == null)
            {
                currentDungeon = Root.AddComponent <Dungeon>();
            }

            currentDungeon.DebugRender = DebugRender;
            currentDungeon.PreGenerateDungeon(this);

            Clear(false);
            targetLength = Mathf.RoundToInt(DungeonFlow.Length.GetRandom(RandomStream) * LengthMultiplier);
            targetLength = Mathf.Max(targetLength, 2);

            // Tile Injection
            GenerationStats.BeginTime(GenerationStatus.TileInjection);

            if (tilesPendingInjection == null)
            {
                tilesPendingInjection = new List <InjectedTile>();
            }
            else
            {
                tilesPendingInjection.Clear();
            }

            injectedTiles.Clear();
            GatherTilesToInject();

            // Pre-Processing
            GenerationStats.BeginTime(GenerationStatus.PreProcessing);
            PreProcess();

            // Main Path Generation
            GenerationStats.BeginTime(GenerationStatus.MainPath);
            yield return(Wait(GenerateMainPath()));

            // We may have had to retry when generating the main path, if so, the status will be either Complete or Failed and we should exit here
            if (Status == GenerationStatus.Complete || Status == GenerationStatus.Failed)
            {
                yield break;
            }

            // Branch Paths Generation
            GenerationStats.BeginTime(GenerationStatus.Branching);
            yield return(Wait(GenerateBranchPaths()));

            // If there are any required tiles missing from the tile injection stage, the generation process should fail
            foreach (var tileInjection in tilesPendingInjection)
            {
                if (tileInjection.IsRequired)
                {
                    yield return(Wait(InnerGenerate(true)));

                    yield break;
                }
            }

            // We may have missed some required injected tiles and have had to retry, if so, the status will be either Complete or Failed and we should exit here
            if (Status == GenerationStatus.Complete || Status == GenerationStatus.Failed)
            {
                yield break;
            }

            // Post-Processing
            yield return(Wait(PostProcess()));


            // Waiting one frame so objects are in their expected state
            yield return(null);


            ChangeStatus(GenerationStatus.Complete);

            // Let DungenCharacters know that they should re-check the Tile they're in
            foreach (var character in Component.FindObjectsOfType <DungenCharacter>())
            {
                character.ForceRecheckTile();
            }
        }
Exemple #5
0
        protected virtual void PlaceLocksAndKeys()
        {
            var nodes = currentDungeon.ConnectionGraph.Nodes.Select(x => x.Tile.Node).Where(x => { return(x != null); }).Distinct().ToArray();
            var lines = currentDungeon.ConnectionGraph.Nodes.Select(x => x.Tile.Line).Where(x => { return(x != null); }).Distinct().ToArray();

            Dictionary <Doorway, Key> lockedDoorways = new Dictionary <Doorway, Key>();

            // Lock doorways on nodes
            foreach (var node in nodes)
            {
                foreach (var l in node.Locks)
                {
                    var     tile        = currentDungeon.AllTiles.Where(x => { return(x.Node == node); }).FirstOrDefault();
                    var     connections = currentDungeon.ConnectionGraph.Nodes.Where(x => { return(x.Tile == tile); }).FirstOrDefault().Connections;
                    Doorway entrance    = null;
                    Doorway exit        = null;

                    foreach (var conn in connections)
                    {
                        if (conn.DoorwayA.Tile == tile)
                        {
                            exit = conn.DoorwayA;
                        }
                        else if (conn.DoorwayB.Tile == tile)
                        {
                            entrance = conn.DoorwayB;
                        }
                    }

                    var key = node.Graph.KeyManager.GetKeyByID(l.ID);

                    if (key.Prefab != null)
                    {
                        if (entrance != null && (node.LockPlacement & NodeLockPlacement.Entrance) == NodeLockPlacement.Entrance)
                        {
                            lockedDoorways.Add(entrance, key);
                        }

                        if (exit != null && (node.LockPlacement & NodeLockPlacement.Exit) == NodeLockPlacement.Exit)
                        {
                            lockedDoorways.Add(exit, key);
                        }
                    }
                    else
                    {
                        Debug.LogError("Key with ID " + l.ID + " does not have a prefab to place");
                    }
                }
            }

            // Lock doorways on lines
            foreach (var line in lines)
            {
                var doorways = currentDungeon.ConnectionGraph.Connections.Where(x =>
                {
                    bool isDoorwayAlreadyLocked = lockedDoorways.ContainsKey(x.DoorwayA) || lockedDoorways.ContainsKey(x.DoorwayB);
                    bool doorwayHasLockPrefabs  = x.DoorwayA.Tile.TileSet.LockPrefabs.Count > 0;

                    return(x.DoorwayA.Tile.Line == line &&
                           x.DoorwayB.Tile.Line == line &&
                           !isDoorwayAlreadyLocked &&
                           doorwayHasLockPrefabs);
                }).Select(x => x.DoorwayA).ToList();

                if (doorways.Count == 0)
                {
                    continue;
                }

                foreach (var l in line.Locks)
                {
                    int lockCount = l.Range.GetRandom(RandomStream);
                    lockCount = Mathf.Clamp(lockCount, 0, doorways.Count);

                    for (int i = 0; i < lockCount; i++)
                    {
                        if (doorways.Count == 0)
                        {
                            break;
                        }

                        var doorway = doorways[RandomStream.Next(0, doorways.Count)];
                        doorways.Remove(doorway);

                        if (lockedDoorways.ContainsKey(doorway))
                        {
                            continue;
                        }

                        var key = line.Graph.KeyManager.GetKeyByID(l.ID);
                        lockedDoorways.Add(doorway, key);
                    }
                }
            }

            List <Doorway> locksToRemove = new List <Doorway>();

            foreach (var pair in lockedDoorways)
            {
                var         door = pair.Key;
                var         key  = pair.Value;
                List <Tile> possibleSpawnTiles = new List <Tile>();

                foreach (var t in currentDungeon.AllTiles)
                {
                    if (t.Placement.NormalizedPathDepth >= door.Tile.Placement.NormalizedPathDepth)
                    {
                        continue;
                    }

                    bool canPlaceKey = false;

                    if (t.Node != null && t.Node.Keys.Where(x => { return(x.ID == key.ID); }).Count() > 0)
                    {
                        canPlaceKey = true;
                    }
                    else if (t.Line != null && t.Line.Keys.Where(x => { return(x.ID == key.ID); }).Count() > 0)
                    {
                        canPlaceKey = true;
                    }

                    if (!canPlaceKey)
                    {
                        continue;
                    }

                    possibleSpawnTiles.Add(t);
                }

                var possibleSpawnComponents = possibleSpawnTiles.SelectMany(x => x.GetComponentsInChildren <Component>().OfType <IKeySpawnable>()).ToList();

                if (possibleSpawnComponents.Count == 0)
                {
                    locksToRemove.Add(door);
                }
                else
                {
                    int keysToSpawn = key.KeysPerLock.GetRandom(RandomStream);
                    keysToSpawn = Math.Min(keysToSpawn, possibleSpawnComponents.Count);

                    for (int i = 0; i < keysToSpawn; i++)
                    {
                        int chosenCompID = RandomStream.Next(0, possibleSpawnComponents.Count);
                        var comp         = possibleSpawnComponents[chosenCompID];
                        comp.SpawnKey(key, DungeonFlow.KeyManager);

                        foreach (var k in (comp as Component).GetComponentsInChildren <Component>().OfType <IKeyLock>())
                        {
                            k.OnKeyAssigned(key, DungeonFlow.KeyManager);
                        }

                        possibleSpawnComponents.RemoveAt(chosenCompID);
                    }
                }
            }

            foreach (var door in locksToRemove)
            {
                lockedDoorways.Remove(door);
            }

            foreach (var pair in lockedDoorways)
            {
                pair.Key.RemoveUsedPrefab();
                LockDoorway(pair.Key, pair.Value, DungeonFlow.KeyManager);
            }
        }