protected override Rectangle?SelectNextLot(RectangleIntervalTree placedFragments, SimpleRandom random) { var boundingRectangle = placedFragments.BoundingRectangle; while (_lotSize.Width > boundingRectangle.Width || _lotSize.Height > boundingRectangle.Height) { _lotSize = new Dimensions((byte)(_lotSize.Width - 1), (byte)(_lotSize.Height - 1)); } while (_lotSize.Contains(MinLotSize)) { // TODO: Optimize using // "Polygon Decomposition". Handbook of Computational Geometry. p. 491 or // "Graph-Theoretic Solutions to Computational Geometry Problems" // https://stackoverflow.com/questions/5919298/algorithm-for-finding-the-fewest-rectangles-to-cover-a-set-of-rectangles-without for (var attempt = 0; attempt < LotPlacementAttempts; attempt++) { var x1 = (byte)random.Next(boundingRectangle.TopLeft.X, boundingRectangle.BottomRight.X - _lotSize.Width + 1); var y1 = (byte)random.Next(boundingRectangle.TopLeft.Y, boundingRectangle.BottomRight.Y - _lotSize.Height + 1); var potentialLot = new Rectangle(new Point(x1, y1), (byte)(_lotSize.Width - 1), (byte)(_lotSize.Height - 1)); if (!placedFragments.GetOverlapping(potentialLot).Any()) { return(potentialLot); } } _lotSize = new Dimensions((byte)(_lotSize.Width - 1), (byte)(_lotSize.Height - 1)); } return(null); }
protected abstract Rectangle?SelectNextLot(RectangleIntervalTree placedFragments, SimpleRandom random);
protected void PlaceSurroundingFragments(LevelComponent level, List <Room> rooms) { var manager = level.Entity.Manager; var levelBoundingRectangle = level.BoundingRectangle; var placedFragments = new RectangleIntervalTree(levelBoundingRectangle); var filledArea = 0; var placedRoom = rooms.FirstOrDefault(); if (placedRoom != null) { placedFragments.Insert(placedRoom.BoundingRectangle); filledArea += placedRoom.BoundingRectangle.Area; } var placingConnections = true; while (filledArea / (float)levelBoundingRectangle.Area < Coverage && rooms.Count < MaxRoomCount) { var nextLot = SelectNextLot(placedFragments, level.GenerationRandom); if (nextLot == null) { throw new InvalidOperationException("No more available lots found"); } // For every existing connection to this level that hasn't been connected yet generate a destination fragment. // Then generate up to 3 source fragments, depending on the number of incoming connections // At least 2 to the next branch level if not final var danglingConnection = manager.IncomingConnectionsToLevelRelationship[level.EntityId] .Select(c => c.Connection).FirstOrDefault(c => c.TargetLevelX == null); placingConnections = placingConnections && (danglingConnection != null || manager.ConnectionsToLevelRelationship[level.EntityId] .Count(c => c.Connection.TargetLevelX == null) < 3 || (level.Branch.Length > level.Depth && manager.ConnectionsToLevelRelationship[level.EntityId] .Select(c => manager.FindEntity(c.Connection.TargetLevelId).Level) .Count(l => l.BranchName == level.BranchName && l.Depth == level.Depth + 1) < 2)); var sortedFragments = placingConnections ? level.GenerationRandom.WeightedOrder( ConnectingMapFragment.Loader.GetAsList(), f => f.GetWeight(level, nextLot.Value, danglingConnection)) : (IEnumerable <MapFragment>)level.GenerationRandom.WeightedOrder( NormalMapFragment.Loader.GetAsList(), f => f.GetWeight(level, nextLot.Value)); placedRoom = TryPlace(level, nextLot.Value, sortedFragments); if (placedRoom == null) { if (placingConnections) { placingConnections = false; continue; } throw new InvalidOperationException("No suitable fragments"); } rooms.Add(placedRoom); placedFragments.Insert(placedRoom.BoundingRectangle); filledArea += placedRoom.BoundingRectangle.Area; } // TODO: Fill the empty space with snapping fragments and connect them to overwritable fragments if (rooms.Count > 1) { // TODO: Perf: use a different data structure var connectedRooms = new List <Room> { rooms.First() }; var unconnectedRooms = new List <Room>(rooms.Skip(1).Where(r => r.DoorwayPoints.Count > 0)); while (unconnectedRooms.Count > 0) { var randomConnectedRoom = level.GenerationRandom.Pick(connectedRooms); var unconnectedRoom = randomConnectedRoom.GetOrthogonallyClosest(unconnectedRooms); var closestConnectedRoom = unconnectedRoom.GetOrthogonallyClosest(connectedRooms); if (!Connect(unconnectedRoom, closestConnectedRoom)) { throw new InvalidOperationException("Couldn't connect all rooms"); } connectedRooms.Add(unconnectedRoom); unconnectedRooms.Remove(unconnectedRoom); } } }
protected override Rectangle?SelectNextLot(RectangleIntervalTree placedFragments, SimpleRandom random) => null;