public Doorway PickRandomDoorway(System.Random randomStream, bool mustBeAvailable, DungeonArchetype archetype)
        {
            float  straightenChance = (archetype == null) ? 0.0f : archetype.StraightenChance;
            double rnd = randomStream.NextDouble();

            // Try to pick the opposing doorway, based on archetype's StraightenChance
            if (isOnMainPath && UsedDoorways.Count == 1 && rnd < straightenChance)
            {
                // Find the doorway opposite to the first
                foreach (Doorway d in UnusedDoorways)
                {
                    if (UsedDoorways[0].transform.forward == -d.transform.forward)
                    {
                        return(d);
                    }
                }
            }

            int index = PickRandomDoorwayIndex(randomStream, mustBeAvailable);

            return((index == -1) ? null : AllDoorways[index]);
        }
        internal TilePlacementData(PreProcessTileData preProcessData, bool isOnMainPath, DungeonArchetype archetype, TileSet tileSet, Dungeon dungeon)
        {
            root = (GameObject)GameObject.Instantiate(preProcessData.Prefab);

            Bounds       = preProcessData.Proxy.GetComponent <Collider>().bounds;
            IsOnMainPath = isOnMainPath;

            tile = Root.GetComponent <Tile>();

            if (tile == null)
            {
                tile = Root.AddComponent <Tile>();
            }

            tile.Placement = this;
            tile.Archetype = archetype;
            tile.TileSet   = tileSet;
            tile.Dungeon   = dungeon;

            foreach (var doorway in Root.GetComponentsInChildren <Doorway>(true))
            {
                doorway.Dungeon = dungeon;
                doorway.Tile    = tile;
                AllDoorways.Add(doorway);
            }

            UnusedDoorways.AddRange(AllDoorways);
            root.SetActive(false);
        }
Exemple #3
0
        protected virtual Tile AddTile(Tile attachTo, IEnumerable <TileSet> useableTileSets, float normalizedDepth, DungeonArchetype archetype, TilePlacementResult result = TilePlacementResult.None)
        {
            bool isOnMainPath = (Status == GenerationStatus.MainPath);
            bool isFirstTile  = attachTo == null;

            // Check list of tiles to inject
            InjectedTile chosenInjectedTile        = null;
            int          injectedTileIndexToRemove = -1;

            bool isPlacingSpecificRoom = isOnMainPath && (archetype == null);

            if (tilesPendingInjection != null && !isPlacingSpecificRoom)
            {
                float pathDepth   = (isOnMainPath) ? normalizedDepth : attachTo.Placement.PathDepth / (float)(targetLength - 1);
                float branchDepth = (isOnMainPath) ? 0 : normalizedDepth;

                for (int i = 0; i < tilesPendingInjection.Count; i++)
                {
                    var injectedTile = tilesPendingInjection[i];

                    if (injectedTile.ShouldInjectTileAtPoint(isOnMainPath, pathDepth, branchDepth))
                    {
                        chosenInjectedTile        = injectedTile;
                        injectedTileIndexToRemove = i;

                        break;
                    }
                }
            }


            // Select appropriate tile weights
            IEnumerable <GameObjectChance> chanceEntries;

            if (chosenInjectedTile != null)
            {
                chanceEntries = new List <GameObjectChance>(chosenInjectedTile.TileSet.TileWeights.Weights);
            }
            else
            {
                chanceEntries = useableTileSets.SelectMany(x => x.TileWeights.Weights);
            }


            // Apply constraint overrides
            bool allowRepeatTile = (isFirstTile) ? true : attachTo.AllowImmediateRepeats;

            if (OverrideAllowImmediateRepeats)
            {
                allowRepeatTile = AllowImmediateRepeats;
            }

            bool allowRotation = (isFirstTile) ? false : attachTo.AllowRotation;

            if (OverrideAllowTileRotation)
            {
                allowRotation = AllowTileRotation;
            }



            DoorwayPairFinder doorwayPairFinder = new DoorwayPairFinder()
            {
                RandomStream            = RandomStream,
                Archetype               = archetype,
                GetTileTemplateDelegate = GetTileTemplate,
                IsOnMainPath            = isOnMainPath,
                NormalizedDepth         = normalizedDepth,
                PreviousTile            = attachTo,
                PreviousPrefab          = (attachTo == null) ? null : placedTilePrefabs[attachTo],
                UpVector      = UpVector,
                AllowRotation = allowRotation,
                TileWeights   = new List <GameObjectChance>(chanceEntries),

                IsTileAllowedPredicate = (Tile previousTile, GameObject previousPrefab, GameObject prefab, ref float weight) =>
                {
                    bool isRepeat = (prefab == previousPrefab);

                    return(!isRepeat || allowRepeatTile);
                },
            };

            int?maxPairingAttempts             = (UseMaximumPairingAttempts) ? (int?)MaxPairingAttempts : null;
            Queue <DoorwayPair> pairsToTest    = doorwayPairFinder.GetDoorwayPairs(maxPairingAttempts);
            TilePlacementResult lastTileResult = TilePlacementResult.NoValidTile;
            Tile createdTile = null;

            while (pairsToTest.Count > 0)
            {
                var pair = pairsToTest.Dequeue();

                lastTileResult = TryPlaceTile(pair, archetype, out createdTile);

                if (lastTileResult == TilePlacementResult.None)
                {
                    break;
                }
                else
                {
                    AddTilePlacementResult(lastTileResult);
                }
            }

            // Successfully placed the tile
            if (lastTileResult == TilePlacementResult.None)
            {
                // We've successfully injected the tile, so we can remove it from the pending list now
                if (chosenInjectedTile != null)
                {
                    injectedTiles[createdTile] = chosenInjectedTile;
                    tilesPendingInjection.RemoveAt(injectedTileIndexToRemove);

                    if (isOnMainPath)
                    {
                        targetLength++;
                    }
                }

                return(createdTile);
            }
            else
            {
                return(null);
            }
        }
Exemple #4
0
        protected TilePlacementResult TryPlaceTile(DoorwayPair pair, DungeonArchetype archetype, out Tile tile)
        {
            tile = null;

            var toTemplate  = pair.NextTemplate;
            var fromDoorway = pair.PreviousDoorway;

            if (toTemplate == null)
            {
                return(TilePlacementResult.TemplateIsNull);
            }

            int toDoorwayIndex = pair.NextTemplate.Doorways.IndexOf(pair.NextDoorway);

            if (fromDoorway != null)
            {
                // Move the proxy object into position
                GameObject toProxyDoor = toTemplate.ProxySockets[toDoorwayIndex];
                UnityUtil.PositionObjectBySocket(toTemplate.Proxy, toProxyDoor, fromDoorway.gameObject);

                Bounds proxyBounds = toTemplate.Proxy.GetComponent <Collider>().bounds;

                // Check if the new tile is outside of the valid bounds
                if (RestrictDungeonToBounds && !TilePlacementBounds.Contains(proxyBounds))
                {
                    return(TilePlacementResult.OutOfBounds);
                }

                // Check if the new tile is colliding with any other
                if (IsCollidingWithAnyTile(proxyBounds))
                {
                    return(TilePlacementResult.TileIsColliding);
                }
            }

            TilePlacementData newTile = new TilePlacementData(toTemplate, (Status == GenerationStatus.MainPath), archetype, pair.NextTileSet, currentDungeon);

            if (newTile == null)
            {
                return(TilePlacementResult.NewTileIsNull);
            }

            if (newTile.IsOnMainPath)
            {
                if (pair.PreviousTile != null)
                {
                    newTile.PathDepth = pair.PreviousTile.Placement.PathDepth + 1;
                }
            }
            else
            {
                newTile.PathDepth   = pair.PreviousTile.Placement.PathDepth;
                newTile.BranchDepth = (pair.PreviousTile.Placement.IsOnMainPath) ? 0 : pair.PreviousTile.Placement.BranchDepth + 1;
            }

            if (fromDoorway != null)
            {
                newTile.Root.transform.parent = Root.transform;
                Doorway toDoorway = newTile.AllDoorways[toDoorwayIndex];

                UnityUtil.PositionObjectBySocket(newTile.Root, toDoorway.gameObject, fromDoorway.gameObject);
                currentDungeon.MakeConnection(fromDoorway, toDoorway, RandomStream);
            }
            else
            {
                newTile.Root.transform.parent        = Root.transform;
                newTile.Root.transform.localPosition = Vector3.zero;
            }

            currentDungeon.AddTile(newTile.Tile);

            if (!newTile.Tile.OverrideAutomaticTileBounds)
            {
                newTile.RecalculateBounds(IgnoreSpriteBounds, UpVector);
            }
            else
            {
                newTile.Bounds      = newTile.Tile.transform.TransformBounds(newTile.Tile.TileBoundsOverride);
                newTile.LocalBounds = newTile.Tile.TileBoundsOverride;
            }

            if (PlaceTileTriggers)
            {
                newTile.Tile.AddTriggerVolume();
                newTile.Root.layer = TileTriggerLayer;
            }

            allDoorways.AddRange(newTile.AllDoorways);

            tile = newTile.Tile;
            placedTilePrefabs[tile] = toTemplate.Prefab;

            return(TilePlacementResult.None);
        }
Exemple #5
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
        }