/// <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)); }
public void FadeOutRoomDone() { Destroy(fadingGameObject); fadingGameObject = null; fadingProceduralRoom = null; }
/// <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); }
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); }
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); }
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; } } }
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); }
public RoomMeta(ProceduralRoom room) { Room = room; InFactor = 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); }
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)); }
public static string GetName(this ProceduralRoom module) { return("M" + module.GetHashCode().ToString("X")); }
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; }
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; } } }
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); } }