コード例 #1
0
        /// <summary>
        /// Adds the room to the construction and clears the open list of invalid entries.
        /// </summary>
        /// <param name="room"></param>
        private void CommitRoom(ProceduralRoom room)
        {
            // Add to the construction
            m_construction.AddRoom(room);

            foreach (var mount in room.MountPoints)
            {
                var attach = mount.AttachedTo;
                if (attach == null)
                {
                    m_openMountPoints.Enqueue(mount);
                    continue;
                }
                // Clear the open list of invalid entries
                foreach (var other in m_possibleRooms[mount])
                {
                    other.InFactor--;
                    if (other.InFactor == 0)
                    {
                        m_openRooms.Remove(other.Key);
                    }
                }
                m_possibleRooms.Remove(mount);
            }
            m_openRooms.Remove(new RoomKey(room.Transform, room.Part));
        }
コード例 #2
0
    public void FadeOutRoomDone()
    {
        Destroy(fadingGameObject);

        fadingGameObject     = null;
        fadingProceduralRoom = null;
    }
コード例 #3
0
        /// <summary>
        /// Registers the given room as a possibility if it isn't already registered.
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="part"></param>
        private RoomMeta RegisterKey(MatrixI transform, PartFromPrefab part)
        {
            var      key = new RoomKey(transform, part);
            RoomMeta room;

            if (!m_openRooms.TryGetValue(key, out room))
            {
                var ent = new ProceduralRoom();
                ent.Init(transform, part);
                m_openRooms[key] = room = new RoomMeta(ent);
            }
            else if (room.Nonce == m_nonce)
            {
                return(room);
            }
            room.Nonce    = m_nonce;
            room.InFactor = 0;
            foreach (var mount in room.Room.MountPoints)
            {
                var other = mount.AttachedToIn(m_construction);
                if (other == null)
                {
                    continue;
                }
                room.InFactor++;
                m_possibleRooms.Add(other, room);
            }
            return(room);
        }
コード例 #4
0
        public ConstructionCopy(ProceduralRoom room, RoomRemapper remapper = null)
        {
            Logger = room.Owner.Logger.Root().CreateProxy(GetType().Name);
            var i = room.Part.PrimaryGrid;
            var o = new MyObjectBuilder_CubeGrid
            {
                GridSizeEnum           = i.GridSizeEnum,
                IsStatic               = true,
                DampenersEnabled       = true,
                Handbrake              = true,
                DisplayName            = room.Owner.Seed.Name,
                DestructibleBlocks     = true,
                IsRespawnGrid          = false,
                Editable               = true,
                PersistentFlags        = MyPersistentEntityFlags2.Enabled | MyPersistentEntityFlags2.InScene | MyPersistentEntityFlags2.CastShadows,
                PositionAndOrientation = new MyPositionAndOrientation(GridCreator.WorldTransformFor(room.Owner))
            };

            BoundingBox = BoundingBoxD.CreateInvalid();

            Construction = room.Owner;
            PrimaryGrid  = o;
            AuxGrids     = new List <MyObjectBuilder_CubeGrid>();
            m_remapper   = remapper ?? new RoomRemapper(Logger.Root());

            var iwatch = new Stopwatch();

            m_remapper.Remap(room, this);
            Logger.Debug("Added room {3} of {0} blocks with {1} aux grids in {2}", room.Part.PrimaryGrid.CubeBlocks.Count, room.Part.Prefab.CubeGrids.Length - 1, iwatch.Elapsed, room.Part.Name);
        }
コード例 #5
0
ファイル: WelcomeRoom.cs プロジェクト: schmidtec/VRDesignLab
    public void FadeOutRoomDone()
    {
        Destroy(fadingGameObject);

        fadingGameObject = null;
        fadingProceduralRoom = null;
    }
コード例 #6
0
        public void AppendRoom(ProceduralRoom room)
        {
            var iwatch = new Stopwatch();

            m_remapper.Remap(room, this);
            Logger.Debug("Added room {3} of {0} blocks with {1} aux grids in {2}", room.Part.PrimaryGrid.CubeBlocks.Count, room.Part.Prefab.CubeGrids.Length - 1, iwatch.Elapsed, room.Part.Name);
        }
コード例 #7
0
    void FadeOutRoom()
    {
        if (proceduralRoom != null)
        {
            if (fadingGameObject == null)
            {
                fadingGameObject = proceduralRoom;
                proceduralRoom   = null;

                fadingProceduralRoom = fadingGameObject.GetComponent <ProceduralRoom>();

                iTween.ValueTo(gameObject, iTween.Hash("from", 1f, "to", 0f, "easetype", iTween.EaseType.easeOutExpo, "onupdate", "FadeOutRoomUpdate", "time", 2f, "oncomplete", "FadeOutRoomDone"));
            }
            else
            {
                // don't bother fading if already in progress
                Destroy(proceduralRoom);
                proceduralRoom = null;
            }
        }
    }
コード例 #8
0
        private string ProcessDebugPart(CommandFeedback feedback, string partName)
        {
            var part = m_partManager.FirstOrDefault(test => test.Prefab.Id.SubtypeName.ToLower().Contains(partName.ToLower()));

            if (part == null)
            {
                return("Unable to find part with name \"" + partName + "\"");
            }
            var position = MyAPIGateway.Session.Camera.Position + MyAPIGateway.Session.Camera.WorldMatrix.Forward * 100;
            var seed     = new ProceduralConstructionSeed(new ProceduralFactionSeed("dummy", 0), new Vector4D(position, 0.5), null, 0);

            MyAPIGateway.Parallel.Start(() =>
            {
                var construction = new ProceduralConstruction(RootLogger, seed);
                var room         = new ProceduralRoom();
                room.Init(new MatrixI(Base6Directions.Direction.Forward, Base6Directions.Direction.Up), part);
                construction.AddRoom(room);
                var remapper = new RoomRemapper(RootLogger)
                {
                    DebugRoomColors = true
                };
                var grids = GridCreator.RemapAndBuild(construction, remapper);
                if (grids == null)
                {
                    return;
                }
                MyAPIGateway.Utilities.InvokeOnGameThread(() =>
                {
                    var component = grids.SpawnAsync();
                    if (component != null)
                    {
                        component.ForceDebugDraw = true;
                    }
                });
            });
            return(null);
        }
コード例 #9
0
 public RoomMeta(ProceduralRoom room)
 {
     Room     = room;
     InFactor = 0;
 }
コード例 #10
0
        private bool CollidesPredictive(ProceduralRoom room, bool testMounts, bool testOptional)
        {
            // Buildable?
            if (m_construction.Intersects(room, testOptional))
            {
                return(true);
            }
            if (!testMounts)
            {
                return(false);
            }

            m_invokers.Clear();
            // Reject if this will block another mount point, or one of our mount points would be blocked.
            // Quick test based on the mounting blocks themselves.
            foreach (var point in room.MountPoints)
            {
                var mount = point.AttachedToIn(m_construction);
                if (mount != null)
                {
                    m_invokers.Add(mount);
                }
                else if (point.MountLocations.Any(m_construction.CubeExists))
                {
                    return(true);
                }
            }
            foreach (var other in m_construction.Rooms)
            {
                if (other != room)
                {
                    foreach (var point in other.MountPoints)
                    {
                        if (point.AttachedToIn(m_construction) == null)
                        {
                            foreach (var block in point.MountPoint.Blocks)
                            {
                                var pos = other.PrefabToGrid(block.MountLocation);
                                if (!room.CubeExists(pos))
                                {
                                    continue;
                                }
                                var mountBlock = room.GetMountPointBlockAt(pos);
                                if (mountBlock == null || !mountBlock.TypeEquals(block) || pos != room.PrefabToGrid(mountBlock.AnchorLocation))
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                }
            }

            // Reject if this will block another mount point, or one of our mount points would blocked.  Use expensive test.
            foreach (var point in room.MountPoints)
            {
                if (point.AttachedToIn(m_construction) == null)
                {
                    var oppos = point.MountPoint.SmallestTerminalAttachment;
                    if (oppos.Item1 == null)
                    {
                        continue;
                    }
                    var     pos = Utilities.Multiply(oppos.Item2, room.Transform);
                    MatrixI ipos;
                    MatrixI.Invert(ref pos, out ipos);
                    if (m_construction.Intersects(oppos.Item1, pos, ipos, testOptional, true, room))
                    {
                        return(true);
                    }
                }
            }
            // Compare to all other unused mount points.
            foreach (var other in m_construction.Rooms)
            {
                if (other != room)
                {
                    foreach (var point in other.MountPoints)
                    {
                        if (!m_invokers.Contains(point) && point.AttachedTo == null)
                        {
                            // TODO we actually have this data pre-computed if we wanted to use that.
                            var oppos = point.MountPoint.SmallestTerminalAttachment;
                            if (oppos.Item1 == null)
                            {
                                continue;
                            }
                            var     pos = Utilities.Multiply(oppos.Item2, other.Transform);
                            MatrixI ipos;
                            MatrixI.Invert(ref pos, out ipos);
                            if (PartMetadata.Intersects(room.Part, room.Transform, room.InvTransform,
                                                        other.Part, pos, ipos, testOptional, true))
                            {
                                return(true);
                            }
                        }
                    }
                }
            }
            return(false);
        }
コード例 #11
0
        public void Remap(ProceduralRoom room, ConstructionCopy dest)
        {
            if (dest.PrimaryGrid.GridSizeEnum != room.Part.PrimaryCubeSize)
            {
                throw new ArgumentException("Primary grid cube size and room's primary cube size differ");
            }
            // Setup remap parameters
            {
                var localTransform = Remap <GridRemap_LocalTransform>();
                localTransform.LocalTransform = room.Transform;
            }

            {
                var naming = Remap <GridRemap_Names>();
                naming.PrefixFor(GridRemap_Names.RemapType.All, room.GetName() + " ");
            }

            {
                var coloring = Remap <Coloring>();
                coloring.OverrideColor      = DebugRoomColors ? (SerializableVector3?)Utilities.NextColor.ColorToHSV() : null;
                coloring.HueRotation        = room.Owner.Seed.Faction.HueRotation;
                coloring.SaturationModifier = room.Owner.Seed.Faction.SaturationModifier;
                coloring.ValueModifier      = room.Owner.Seed.Faction.ValueModifier;
            }

            {
                var ownership = Remap <GridRemap_Ownership>();
                var faction   = room.Owner.Seed.Faction.GetOrCreateFaction();
                ownership.OwnerID              = faction?.FounderId ?? 0;
                ownership.ShareMode            = MyOwnershipShareModeEnum.Faction;
                ownership.UpgradeShareModeOnly = true;
            }

            var worldTransform = Remap <GridRemap_WorldTransform>();
            {
                var roomTransformScaled = room.Transform.GetFloatMatrix();
                roomTransformScaled.Translation *= MyDefinitionManager.Static.GetCubeSize(dest.PrimaryGrid.GridSizeEnum);

                var prefabPrimaryGridNewWorldMatrix = Matrix.Multiply(roomTransformScaled,
                                                                      dest.PrimaryGrid.PositionAndOrientation?.GetMatrix() ?? MatrixD.Identity);
                var prefabPrimaryGridOldWorldMatrix = room.Part.PrimaryGrid.PositionAndOrientation?.GetMatrix() ??
                                                      MatrixD.Identity;


                var prefabOldToNew = Matrix.Multiply(Matrix.Invert(prefabPrimaryGridOldWorldMatrix),
                                                     prefabPrimaryGridNewWorldMatrix);

                worldTransform.WorldTransform      = prefabOldToNew;
                worldTransform.WorldLinearVelocity = dest.PrimaryGrid.LinearVelocity;
            }

            // Grab OB copies
            var timer = new Stopwatch();

            timer.Restart();
            var roomGrid   = CloneUtilities.CloneFast(room.Part.PrimaryGrid);
            var otherGrids = room.Part.Prefab.CubeGrids.Where(x => x != room.Part.PrimaryGrid).Select(CloneUtilities.CloneFast).ToList();
            var allGrids   = new List <MyObjectBuilder_CubeGrid>(otherGrids)
            {
                roomGrid
            };

            Logger.Debug("Cloned {0} grids in {1}", allGrids.Count, timer.Elapsed);


            // Remap entity IDs
            timer.Restart();
            MyAPIGateway.Entities.RemapObjectBuilderCollection(allGrids);
            // If we have a primary ID copy it now.
            if (dest.PrimaryGrid.EntityId != 0)
            {
                var constRemapID = new ConstantEntityRemap(new Dictionary <long, long> {
                    [roomGrid.EntityId] = dest.PrimaryGrid.EntityId
                });
                // Anything referring to the root grid's entity ID needs to be changed to the old grid.
                foreach (var c in allGrids)
                {
                    c.Remap(constRemapID);
                }
            }
            else // otherwise, skip
            {
                dest.PrimaryGrid.EntityId = roomGrid.EntityId;
            }
            Logger.Debug("Remapped {0} grid IDs in {1}", allGrids.Count, timer.Elapsed);

            // Apply remap operators
            m_allPre.RemapAndReset(allGrids);
            m_primary.RemapAndReset(new[] { roomGrid });
            m_auxiliary.RemapAndReset(otherGrids);
            m_allPost.RemapAndReset(allGrids);

            // Merge data into primary grid from room grid
            dest.PrimaryGrid.CubeBlocks.Capacity += roomGrid.CubeBlocks.Count;
            dest.PrimaryGrid.CubeBlocks.AddRange(roomGrid.CubeBlocks);

            dest.PrimaryGrid.BlockGroups.Capacity += roomGrid.BlockGroups.Count;
            dest.PrimaryGrid.BlockGroups.AddRange(roomGrid.BlockGroups);

            // Seems suboptimal?  Can we transform this and only invalidate ones on a room border?
            dest.PrimaryGrid.ConveyorLines.Clear();

            // Not even going to try.
            dest.PrimaryGrid.OxygenAmount = null;
            dest.PrimaryGrid.Skeleton     = null;

            // Add aux grids
            dest.AuxGrids.AddRange(otherGrids);

            dest.BoundingBox = BoundingBoxD.CreateMerged(dest.BoundingBox, Utilities.TransformBoundingBox((BoundingBoxD)room.BoundingBoxBoth, worldTransform.WorldTransform));
        }
コード例 #12
0
 public static string GetName(this ProceduralRoom module)
 {
     return("M" + module.GetHashCode().ToString("X"));
 }
コード例 #13
0
    public ProceduralRoom(ProceduralRoom previousRoom) : base(previousRoom)
    {
        _unityObject = new GameObject("ProceduralRoom" + _index);

        GameObject bottomSpriteObject = new GameObject("bottom sprite");

        bottomSpriteObject.transform.parent   = _unityObject.transform;
        bottomSpriteObject.transform.position = new Vector3(_centerX, -4.76f);
        bottomSpriteObject.AddComponent <SpriteRenderer>().sprite = RoomGenerator.StaticBottomOfRoomSprite;


        float       halfRoomWidth     = 50.28f / 2; //because we are using a background with 5028 pixels width
        const float platformWidth     = 6.285f;     //(notice platformWidth * 8 = roomWidth)  , also 1356 * scale= 628.5, scale= 0.4635
        float       halfPlatformWidth = platformWidth / 2;
        const float platformHeight    = 0.616455f;

        float roomX = _centerX;

        Platform previousPlatform = previousRoom == null ? null : previousRoom.LastPlatform;  //the previous platform is the last platform of the previous room


        Transform roomTransform = _unityObject.transform;

        for (int i = 0; i < 8; i++)
        {
            float                  platformX    = roomX - halfRoomWidth + halfPlatformWidth + platformWidth * i; //x is easy as we know the position of the room and the relative position of the platform inside the room
            float                  platformY    = -3.8f;                                                         //it is important that we initialize this to -3.8f. This is the desired y value for the very first platform of our game (for this platform previousplatform=null)
            Platform.Type          type         = Platform.Type.Ordinary;
            ItemThatSitsOnPlatform attachedItem = null;


            if (previousPlatform != null)
            {
                platformY = previousPlatform.Y;


                switch (previousPlatform.MyType)
                {
                case Platform.Type.Gap:
                    type = Platform.Type.Small;
                    //override platformx calculation
                    platformX = roomX - halfRoomWidth + platformWidth * i;
                    break;

                case Platform.Type.Small:
                    type = Platform.Type.Ordinary;
                    break;

                case Platform.Type.Ordinary:

                    //check for gap eligibility
                    if (
                        _index > 0 &&  //not first room, we don't want any gaps and moving platforms in the very first toom
                        i >= 1 && i <= 5 &&     //platforms 0, 6 and 7 cannot be gaps
                        previousPlatform.AttachedItem == null &&
                        Random.Range(0, 4) == 0 //let's say 25% probability of gap
                        )
                    {                           //eligible
                        type = Platform.Type.Gap;
                    }
                    else
                    {    //no gap
                        type = Platform.Type.Ordinary;

                        //override platformy calculation
                        //we want a y that is the y of the previous platform plus/minus a small random value
                        //the only exception is when the previous platform had a ponger on it. We want the new platform to be placed much higher than usual
                        if (previousPlatform.AttachedItem != null && previousPlatform.AttachedItem.RequiresSufficientSpaceAbove)
                        {
                            platformY = previousPlatform.Y + 4 * platformHeight;
                        }
                        else
                        {
                            int upSameOrDown = Random.Range(0, 3);

                            switch (upSameOrDown)
                            {
                            case 0:         //up
                                platformY = previousPlatform.Y + platformHeight;

                                if (platformY > 2f)        //out of bounds, go down instead
                                {
                                    platformY = previousPlatform.Y - platformHeight;
                                }
                                break;

                            case 1:         //same level
                                platformY = previousPlatform.Y;
                                break;

                            case 2:         //down
                                platformY = previousPlatform.Y - platformHeight;

                                if (platformY < -3.8f)        //out of bounds, go up instead
                                {
                                    platformY = previousPlatform.Y + platformHeight;
                                }
                                break;
                            }

                            //check for attached item eligibility
                            //check if we should attach an object to the top of the platform
                            if (
                                _index > 0                            //don't add any objects to the first room so the player adjusts to the gameplay mechanics
                                &&
                                previousPlatform.AttachedItem == null //don't add objects to two platforms in a row
                                &&
                                Random.Range(0, 4) == 0               //25% probability of object
                                )
                            {                                         //eligible
                                int typeIndex = Random.Range(0, RoomGenerator.StaticItemsThatSitOnPlatforms.Length);
                                ItemThatSitsOnPlatform item = RoomGenerator.StaticItemsThatSitOnPlatforms[typeIndex];

                                if (item.RequiresSufficientSpaceAbove)
                                {    //we want to add a ponger. But we should be careful: when we add a ponger the next platform is placed higher than usual.
                                     //this means that in order to add a ponger we should first make sure that there is indeed sufficient space above

                                    if (platformY > 1)
                                    {    //nope, we are way too high for a ponger, instantiate another type
                                        int anotherTypeIndex;
                                        do
                                        {
                                            anotherTypeIndex = Random.Range(0, RoomGenerator.StaticItemsThatSitOnPlatforms.Length);
                                        } while (anotherTypeIndex == typeIndex);

                                        typeIndex = anotherTypeIndex;
                                        item      = RoomGenerator.StaticItemsThatSitOnPlatforms[typeIndex];
                                    }
                                }

                                //now we know what item to add
                                attachedItem = item;
                                //instantiate it
                                GameObject go = Object.Instantiate(item.Prefab, roomTransform);

                                //let's determine its position
                                //to have the object perfectly sit on top of the platform we should move it up a bit.
                                //By how much? ...   By half its height + half the platform's height (constant 0.308227f)
                                //(Oh and it is important that we use the instantiated object for that, not the prefab. The prefab has no bounds)
                                //so:
                                float verticalOffset = go.GetComponent <Collider2D>().bounds.extents.y + 0.308227f;

                                float horizontalOffset = 0.0f;
                                if (item.AutoDetermineHorizontalOffset)
                                {    //e.g. spike
                                     //when we go to a spike platform that is higher than the previous one it is difficult to avoid the spike, so we move the spike to the right
                                     //when we go to a spike platform that is lower than the previous one it is difficult to avoid the spike, so we move the spike to the left
                                    if (platformY > previousPlatform.Y)
                                    {    //new platform is higher, so move the spike right
                                        horizontalOffset = 2.5f;
                                    }
                                    else if (platformY < previousPlatform.Y)
                                    {    //move spike left
                                        horizontalOffset = -2f;
                                    }
                                }

                                //starting with its platform's position
                                go.transform.position = new Vector3(platformX + horizontalOffset, platformY + verticalOffset);
                            }
                        }
                    }
                    break;
                }
            }



            //we instantiate the platform and also make it previousplatform for the next room
            previousPlatform = new Platform(platformX, platformY, type, attachedItem, roomTransform);
        }//for platform


        LastPlatform = previousPlatform;
    }
コード例 #14
0
ファイル: WelcomeRoom.cs プロジェクト: schmidtec/VRDesignLab
    void FadeOutRoom()
    {
        if (proceduralRoom != null)
        {
          if (fadingGameObject == null)
          {
        fadingGameObject = proceduralRoom;
        proceduralRoom = null;

        fadingProceduralRoom = fadingGameObject.GetComponent<ProceduralRoom>();

        iTween.ValueTo(gameObject, iTween.Hash("from", 1f, "to", 0f, "easetype", iTween.EaseType.easeOutExpo, "onupdate", "FadeOutRoomUpdate", "time", 2f, "oncomplete", "FadeOutRoomDone"));
          }
          else
          {
        // don't bother fading if already in progress
        Destroy(proceduralRoom);
        proceduralRoom = null;
          }
        }
    }
コード例 #15
0
        public bool GenerateFromSeed(ProceduralConstructionSeed seed, ref ProceduralConstruction construction, int?roomCount = null)
        {
            Ob_ProceduralConstructionSeed dbSeed;
            Ob_ProceduralConstruction     dbBlueprint;
            Ob_ProceduralFaction          dbFaction;

            if (m_database.TryGetBuildingBlueprint(seed.Seed, out dbSeed, out dbBlueprint) &&
                dbSeed != null &&
                m_database.TryGetFaction(dbSeed.FactionSeed, out dbFaction) && dbFaction != null)
            {
                seed = new ProceduralConstructionSeed(new ProceduralFactionSeed(dbFaction), seed.Location, dbSeed);
                if (construction == null)
                {
                    construction = new ProceduralConstruction(RootLogger, seed);
                }
                if (dbBlueprint != null)
                {
                    this.Debug("Cache hit for {0}", seed.Seed);
                    if (construction.Init(PartManager, dbBlueprint))
                    {
                        return(true);
                    }
                    this.Debug("Cache invalidated for {0}.  Could not find: {1}", seed.Seed, string.Join(", ", dbBlueprint.Rooms.Where(x => PartManager.LoadNullable(x.PrefabID) == null)));
                    construction.Clear();
                }
            }
            try
            {
                var watch = new Stopwatch();
                watch.Reset();
                watch.Start();
                if (Settings.DebugGenerationResults)
                {
                    Log(MyLogSeverity.Debug, "Seeded construction\n{0}", seed.ToString());
                }
                if (construction == null)
                {
                    construction = new ProceduralConstruction(RootLogger, seed);
                }
                // Seed the generator
                if (!construction.Rooms.Any())
                {
                    var parts = PartManager.ToList();
                    var part  = parts[(int)Math.Floor(parts.Count * seed.DeterministicNoise(1234567))];
                    var room  = new ProceduralRoom();
                    room.Init(new MatrixI(Base6Directions.Direction.Forward, Base6Directions.Direction.Up), part);
                    construction.AddRoom(room);
                    if (Settings.DebugGenerationStages || Settings.DebugGenerationResults)
                    {
                        this.Debug("Added {0} (number {1}) at {2}.", room.Part.Name, construction.Rooms.Count(), room.BoundingBox.Center);
                    }
                }
                var scorePrev           = construction.ComputeErrorAgainstSeed();
                var scoreStableTries    = 0;
                var fastGrowth          = (roomCount / 3) ?? (1 + (int)Math.Sqrt(seed.Population / 10f));
                var absoluteRoomsRemain = fastGrowth * 3;
                var gen = new StationGenerator(this, construction);
                while (absoluteRoomsRemain-- > 0)
                {
                    var currentRoomCount = construction.Rooms.Count();
                    if (roomCount.HasValue && currentRoomCount >= roomCount.Value)
                    {
                        break;
                    }
                    if (!roomCount.HasValue && scoreStableTries > 3)
                    {
                        break;
                    }
                    if (BlockLimit > 0 && construction.BlockSetInfo.BlockCountByType.Sum(x => x.Value) >= BlockLimit)
                    {
                        Log(MyLogSeverity.Warning, "Quit because we exceeded the block limit");
                        break;
                    }
                    if (!gen.StepGeneration(fastGrowth > 0 ? 2 : 0))
                    {
                        break;
                    }
                    fastGrowth--;
                    var scoreNow = construction.ComputeErrorAgainstSeed();
                    if (scoreNow >= scorePrev)
                    {
                        scoreStableTries++;
                    }
                    else
                    {
                        scoreStableTries = 0;
                    }
                    scorePrev = scoreNow;
                }
                // Give it plenty of tries to close itself
                if (true)
                {
                    var remainingMounts = construction.Rooms.SelectMany(x => x.MountPoints).Count(y => y.AttachedTo == null);
                    var triesToClose    = remainingMounts * 2 + 2;
                    if (Settings.DebugGenerationResults)
                    {
                        Log(MyLogSeverity.Debug, "There are {0} remaining mounts.  Giving it {1} tries to close itself.", remainingMounts, triesToClose);
                    }
                    var outOfOptions = false;
                    for (var i = 0; i < triesToClose; i++)
                    {
                        if (!gen.StepGeneration(-10))
                        {
                            outOfOptions = true;
                            break;
                        }
                    }
                    remainingMounts = construction.Rooms.SelectMany(x => x.MountPoints).Count(y => y.AttachedTo == null);
                    if (remainingMounts > 0)
                    {
                        if (Settings.DebugGenerationResults)
                        {
                            Log(MyLogSeverity.Debug, "Now there are {0} remaining mounts.  Trying without hints. Reason: {1}", remainingMounts, outOfOptions ? "Out of options" : "Out of tries");
                        }
                        triesToClose = remainingMounts * 2 + 2;
                        for (var i = 0; i < triesToClose; i++)
                        {
                            if (!gen.StepGeneration(-10, false))
                            {
                                outOfOptions = true;
                                break;
                            }
                        }
                    }
                    remainingMounts = construction.Rooms.SelectMany(x => x.MountPoints).Count(y => y.AttachedTo == null);
                    if (Settings.DebugGenerationResults)
                    {
                        if (remainingMounts > 0)
                        {
                            Log(MyLogSeverity.Debug, "Now there are {0} remaining mounts.  Reason: {1}", remainingMounts, outOfOptions ? "Out of options" : "Out of tries");
                        }
                        else
                        {
                            Log(MyLogSeverity.Debug, "Sucessfully closed all mount points");
                        }
                    }
                }
                if (Settings.DebugGenerationResultsError)
                {
                    using (this.IndentUsing())
                        construction.ComputeErrorAgainstSeed(this.Debug);
                }

                var location = MatrixD.CreateFromQuaternion(seed.Orientation);
                location.Translation = seed.Location;

                var msg = $"Added {construction.Rooms.Count()} rooms; generated in {watch.Elapsed}";
                Log(MyLogSeverity.Debug, msg);
                m_database.StoreBuildingBlueprint(construction);
                return(true);
            }
            catch (ArgumentException e)
            {
                Log(MyLogSeverity.Error, "Failed to generate station.\n{0}", e.ToString());
                return(false);
            }
        }