/// <summary> /// Gets all possible places a new path node can be added. /// </summary> /// <param name="floorPlan"></param> /// <param name="branch">Chooses to branch from a path instead of extending it.</param> /// <returns>All possible RoomHallIndex that can receive an expansion.</returns> public static List <RoomHallIndex> GetPossibleExpansions(FloorPlan floorPlan, bool branch) { List <RoomHallIndex> availableExpansions = new List <RoomHallIndex>(); for (int ii = 0; ii < floorPlan.RoomCount; ii++) { var listHall = new RoomHallIndex(ii, false); List <RoomHallIndex> adjacents = floorPlan.GetRoomHall(listHall).Adjacents; if ((adjacents.Count <= 1) != branch) { availableExpansions.Add(listHall); } } for (int ii = 0; ii < floorPlan.HallCount; ii++) { var listHall = new RoomHallIndex(ii, true); List <RoomHallIndex> adjacents = floorPlan.GetRoomHall(listHall).Adjacents; if ((adjacents.Count <= 1) != branch) { availableExpansions.Add(listHall); } } return(availableExpansions); }
protected static SpawnList <ListPathTraversalNode> GetPossibleExpansions(FloorPlan floorPlan, List <RoomHallIndex> candList) { // get all probabilities. // the probability of an extension is the distance that the target room is from the start room, in rooms var expansions = new SpawnList <ListPathTraversalNode>(); for (int nn = 0; nn < candList.Count; nn++) { // find the room to connect to // go through all sides of all rooms (randomly) RoomHallIndex chosenFrom = candList[nn]; IFloorRoomPlan planFrom = floorPlan.GetRoomHall(chosenFrom); // exhausting all possible directions (randomly) foreach (Dir4 dir in DirExt.VALID_DIR4) { bool forbidExtend = false; foreach (RoomHallIndex adjacent in planFrom.Adjacents) { Rect adjRect = floorPlan.GetRoomHall(adjacent).RoomGen.Draw; if (planFrom.RoomGen.Draw.GetScalar(dir) == adjRect.GetScalar(dir.Reverse())) { forbidExtend = true; break; } } if (!forbidExtend) { // find a rectangle to connect it with ListPathTraversalNode?expandToResult = GetRoomToConnect(floorPlan, chosenFrom, dir); if (expandToResult is ListPathTraversalNode expandTo) { int prb = floorPlan.GetDistance(expandTo.From, expandTo.To); if (prb < 0) { expansions.Add(expandTo, 1); } else if (prb > 0) { expansions.Add(expandTo, prb); } } } } } return(expansions); }
public static Rect GetSupportRect(FloorPlan floorPlan, IRoomGen oldGen, IRoomGen newGen, Dir4 dir, List <RoomHallIndex> adjacentsInDir) { bool vertical = dir.ToAxis() == Axis4.Vert; Rect supportRect = newGen.Draw; supportRect.Start += dir.GetLoc() * supportRect.Size.GetScalar(dir.ToAxis()); supportRect.SetScalar(dir, oldGen.Draw.GetScalar(dir)); IntRange minMax = newGen.Draw.GetSide(dir.ToAxis()); foreach (RoomHallIndex adj in adjacentsInDir) { IRoomGen adjGen = floorPlan.GetRoomHall(adj).RoomGen; IntRange adjMinMax = adjGen.Draw.GetSide(dir.ToAxis()); minMax = new IntRange(Math.Min(adjMinMax.Min, minMax.Min), Math.Max(adjMinMax.Max, minMax.Max)); } IntRange oldMinMax = oldGen.Draw.GetSide(dir.ToAxis()); minMax = new IntRange(Math.Max(oldMinMax.Min, minMax.Min), Math.Min(oldMinMax.Max, minMax.Max)); if (vertical) { supportRect.SetScalar(Dir4.Left, minMax.Min); supportRect.SetScalar(Dir4.Right, minMax.Max); } else { supportRect.SetScalar(Dir4.Up, minMax.Min); supportRect.SetScalar(Dir4.Down, minMax.Max); } return(supportRect); }
public void PlaceRoom(IRandom rand, FloorPlan floorPlan, IRoomGen newGen, RoomHallIndex oldRoomHall) { // first get the adjacents of the removed room Dictionary <Dir4, List <RoomHallIndex> > adjacentsByDir = GetDirectionAdjacents(floorPlan, oldRoomHall); IRoomGen oldGen = floorPlan.GetRoomHall(oldRoomHall).RoomGen; // remove the room; update the adjacents too floorPlan.EraseRoomHall(oldRoomHall); foreach (Dir4 dir in DirExt.VALID_DIR4) { for (int jj = 0; jj < adjacentsByDir[dir].Count; jj++) { RoomHallIndex adjRoomHall = adjacentsByDir[dir][jj]; if (adjRoomHall.IsHall == oldRoomHall.IsHall && adjRoomHall.Index > oldRoomHall.Index) { adjacentsByDir[dir][jj] = new RoomHallIndex(adjRoomHall.Index - 1, adjRoomHall.IsHall); } } } var newAdjacents = new List <RoomHallIndex>(); var supportHalls = new Dictionary <Dir4, IPermissiveRoomGen>(); foreach (Dir4 dir in DirExt.VALID_DIR4) { if (newGen.Draw.GetScalar(dir) == oldGen.Draw.GetScalar(dir)) { newAdjacents.AddRange(adjacentsByDir[dir]); } else if (adjacentsByDir[dir].Count > 0) { Rect supportRect = GetSupportRect(floorPlan, oldGen, newGen, dir, adjacentsByDir[dir]); var supportHall = (IPermissiveRoomGen)this.Halls.Pick(rand).Copy(); supportHall.PrepareSize(rand, supportRect.Size); supportHall.SetLoc(supportRect.Start); supportHalls[dir] = supportHall; } } // add the new room var newRoomInd = new RoomHallIndex(floorPlan.RoomCount, false); floorPlan.AddRoom(newGen, true, newAdjacents.ToArray()); // add supporting halls foreach (Dir4 dir in DirExt.VALID_DIR4) { if (supportHalls.ContainsKey(dir)) { // include an attachment to the newly added room List <RoomHallIndex> adjToAdd = new List <RoomHallIndex> { newRoomInd }; adjToAdd.AddRange(adjacentsByDir[dir]); floorPlan.AddHall(supportHalls[dir], adjToAdd.ToArray()); } } }
private static Dictionary <Dir4, List <RoomHallIndex> > GetDirectionAdjacents(FloorPlan floorPlan, RoomHallIndex oldRoomHall) { var adjacentsByDir = new Dictionary <Dir4, List <RoomHallIndex> >(); IFloorRoomPlan oldPlan = floorPlan.GetRoomHall(oldRoomHall); foreach (Dir4 dir in DirExt.VALID_DIR4) { adjacentsByDir[dir] = new List <RoomHallIndex>(); foreach (RoomHallIndex adjacent in oldPlan.Adjacents) { IFloorRoomPlan adjPlan = floorPlan.GetRoomHall(adjacent); if (oldPlan.RoomGen.Draw.GetScalar(dir) == adjPlan.RoomGen.Draw.GetScalar(dir.Reverse())) { adjacentsByDir[dir].Add(adjacent); } } } return(adjacentsByDir); }
public override void ApplyToPath(IRandom rand, FloorPlan floorPlan) { // choose certain rooms in the list to be special rooms // special rooms are required; so make sure they don't overlap IRoomGen newGen = this.Rooms.Pick(rand).Copy(); Loc size = newGen.ProposeSize(rand); newGen.PrepareSize(rand, size); int factor = floorPlan.DrawRect.Area / newGen.Draw.Area; // TODO: accept smaller rooms to replace // bulldozing the surrounding rooms to get the space var room_indices = new SpawnList <RoomHallIndex>(); for (int ii = 0; ii < floorPlan.RoomCount; ii++) { FloorRoomPlan plan = floorPlan.GetRoomPlan(ii); if (!plan.Immutable && plan.RoomGen.Draw.Width >= newGen.Draw.Width && plan.RoomGen.Draw.Height >= newGen.Draw.Height) { room_indices.Add(new RoomHallIndex(ii, false), ComputeRoomChance(factor, plan.RoomGen.Draw, newGen.Draw)); } } for (int ii = 0; ii < floorPlan.HallCount; ii++) { var roomHall = new RoomHallIndex(ii, true); IFloorRoomPlan plan = floorPlan.GetRoomHall(roomHall); if (plan.RoomGen.Draw.Width >= newGen.Draw.Width && plan.RoomGen.Draw.Height >= newGen.Draw.Height) { room_indices.Add(roomHall, ComputeRoomChance(factor, plan.RoomGen.Draw, newGen.Draw)); } } while (room_indices.Count > 0) { int ind = room_indices.PickIndex(rand); RoomHallIndex oldRoomHall = room_indices.GetSpawn(ind); Dictionary <Dir4, List <RoomHallIndex> > adjacentIndicesByDir = GetDirectionAdjacents(floorPlan, oldRoomHall); var adjacentsByDir = new Dictionary <Dir4, List <IRoomGen> >(); foreach (Dir4 dir in DirExt.VALID_DIR4) { adjacentsByDir[dir] = new List <IRoomGen>(); foreach (RoomHallIndex adj in adjacentIndicesByDir[dir]) { adjacentsByDir[dir].Add(floorPlan.GetRoomHall(adj).RoomGen); } } Loc placement = this.FindPlacement(rand, adjacentsByDir, newGen, floorPlan.GetRoomHall(oldRoomHall).RoomGen); if (placement != new Loc(-1)) { newGen.SetLoc(placement); this.PlaceRoom(rand, floorPlan, newGen, oldRoomHall); GenContextDebug.DebugProgress("Set Special Room"); return; } room_indices.RemoveAt(ind); } }
public static void AddLegalPlacements(SpawnList <Loc> possiblePlacements, FloorPlan floorPlan, RoomHallIndex indexFrom, IRoomGen roomFrom, IRoomGen room, Dir4 expandTo) { bool vertical = expandTo.ToAxis() == Axis4.Vert; // this scaling factor equalizes the chances of long sides vs short sides int reverseSideMult = vertical ? roomFrom.Draw.Width * room.Draw.Width : roomFrom.Draw.Height * room.Draw.Height; IntRange side = roomFrom.Draw.GetSide(expandTo.ToAxis()); // subtract the room's original size, not the inflated trialrect size side.Min -= (vertical ? room.Draw.Size.X : room.Draw.Size.Y) - 1; Rect tryRect = room.Draw; // expand in every direction // this will create a one-tile buffer to check for collisions tryRect.Inflate(1, 1); int currentScalar = side.Min; while (currentScalar < side.Max) { // compute the location Loc trialLoc = roomFrom.GetEdgeRectLoc(expandTo, room.Draw.Size, currentScalar); tryRect.Start = trialLoc + new Loc(-1, -1); // check for collisions (not counting the rectangle from) List <RoomHallIndex> collisions = floorPlan.CheckCollision(tryRect); // find the first tile in which no collisions will be found int maxCollideScalar = currentScalar; bool collided = false; foreach (RoomHallIndex collision in collisions) { if (collision != indexFrom) { IRoomGen collideRoom = floorPlan.GetRoomHall(collision).RoomGen; // this is the point at which the new room will barely touch the collided room // the +1 at the end will move it into the safe zone maxCollideScalar = Math.Max(maxCollideScalar, vertical ? collideRoom.Draw.Right : collideRoom.Draw.Bottom); collided = true; } } // if no collisions were hit, do final checks and add the room if (!collided) { Loc locTo = roomFrom.GetEdgeRectLoc(expandTo, room.Draw.Size, currentScalar); // must be within the borders of the floor! if (floorPlan.DrawRect.Contains(new Rect(locTo, room.Draw.Size))) { // check the border match and if add to possible placements int chanceTo = FloorPlan.GetBorderMatch(roomFrom, room, locTo, expandTo); if (chanceTo > 0) { possiblePlacements.Add(locTo, chanceTo * reverseSideMult); } } } currentScalar = maxCollideScalar + 1; } }
/// <summary> /// Chooses a node to expand the path from based on the specified branch setting. /// </summary> /// <param name="availableExpansions">todo: describe availableExpansions parameter on ChooseRoomExpansion</param> /// <param name="prepareRoom">todo: describe prepareRoom parameter on ChooseRoomExpansion</param> /// <param name="hallPercent">todo: describe hallPercent parameter on ChooseRoomExpansion</param> /// <param name="rand"></param> /// <param name="floorPlan"></param> /// <returns>A set of instructions on how to expand the path.</returns> public static ListPathBranchExpansion?ChooseRoomExpansion(RoomPrep prepareRoom, int hallPercent, IRandom rand, FloorPlan floorPlan, List <RoomHallIndex> availableExpansions) { if (availableExpansions.Count == 0) { return(null); } for (int ii = 0; ii < 30; ii++) { // choose the next room to add to RoomHallIndex firstExpandFrom = availableExpansions[rand.Next(availableExpansions.Count)]; RoomHallIndex expandFrom = firstExpandFrom; IRoomGen roomFrom = floorPlan.GetRoomHall(firstExpandFrom).RoomGen; // choose the next room to add // choose room size/fulfillables // note: by allowing halls to be picked as extensions, we run the risk of adding dead-end halls // halls should always terminate at rooms? // this means... doubling up with hall+room? bool addHall = rand.Next(100) < hallPercent; IRoomGen hall = null; if (addHall) { hall = prepareRoom(rand, floorPlan, true); // randomly choose a perimeter to assign this to SpawnList <Loc> possibleHallPlacements = new SpawnList <Loc>(); foreach (Dir4 dir in DirExt.VALID_DIR4) { AddLegalPlacements(possibleHallPlacements, floorPlan, expandFrom, roomFrom, hall, dir); } // at this point, all possible factors for whether a placement is legal or not is accounted for // therefor just pick one if (possibleHallPlacements.Count == 0) { continue; } // randomly choose one Loc hallCandLoc = possibleHallPlacements.Pick(rand); // set location hall.SetLoc(hallCandLoc); // change the roomfrom for the upcoming room expandFrom = new RoomHallIndex(-1, false); roomFrom = hall; } IRoomGen room = prepareRoom(rand, floorPlan, false); // randomly choose a perimeter to assign this to SpawnList <Loc> possiblePlacements = new SpawnList <Loc>(); foreach (Dir4 dir in DirExt.VALID_DIR4) { AddLegalPlacements(possiblePlacements, floorPlan, expandFrom, roomFrom, room, dir); } // at this point, all possible factors for whether a placement is legal or not is accounted for // therefore just pick one if (possiblePlacements.Count > 0) { // randomly choose one Loc candLoc = possiblePlacements.Pick(rand); // set location room.SetLoc(candLoc); return(new ListPathBranchExpansion(firstExpandFrom, room, (IPermissiveRoomGen)hall)); } } return(null); }
protected static ListPathTraversalNode?GetRoomToConnect(FloorPlan floorPlan, RoomHallIndex chosenFrom, Dir4 dir) { // extend a rectangle to the border of the floor in the chosen direction bool vertical = dir.ToAxis() == Axis4.Vert; int dirSign = dir.GetLoc().GetScalar(dir.ToAxis()); IRoomGen genFrom = floorPlan.GetRoomHall(chosenFrom).RoomGen; Rect sampleRect = genFrom.Draw; // expand from the start of that border direction to the borders of the floor sampleRect.Start += dir.GetLoc() * sampleRect.Size.GetScalar(dir.ToAxis()); // it doesn't have to be exactly the borders so just add the total size to be sure sampleRect.Expand(dir, vertical ? floorPlan.Size.Y : floorPlan.Size.X); // find the closest room. var chosenTo = new RoomHallIndex(-1, false); foreach (RoomHallIndex collision in floorPlan.CheckCollision(sampleRect)) { Rect collidedRect = floorPlan.GetRoomHall(collision).RoomGen.Draw; // limit the expansion by direction int sampleScalar = sampleRect.GetScalar(dir); int collidedScalar = collidedRect.GetScalar(dir.Reverse()); bool limit = dirSign == Math.Sign(sampleScalar - collidedScalar); if (limit) { // update the boundaries sampleRect.SetScalar(dir, collidedScalar); chosenTo = collision; } } // if it didn't collide with ANYTHING, then quit if (chosenTo.Index == -1) { return(null); } IRoomGen genTo = floorPlan.GetRoomHall(chosenTo).RoomGen; // narrow the rectangle if touching something on the side // widen the rectangle by width Rect widthRect = sampleRect; widthRect.Inflate(vertical ? 1 : 0, vertical ? 0 : 1); bool retractLeft = false; bool retractRight = false; Dir4 leftDir = DirExt.AddAngles(dir, Dir4.Left); Dir4 rightDir = DirExt.AddAngles(dir, Dir4.Right); foreach (RoomHallIndex collision in floorPlan.CheckCollision(widthRect)) { Rect collidedRect = floorPlan.GetRoomHall(collision).RoomGen.Draw; if (!retractLeft) { if (collidedRect.GetScalar(leftDir.Reverse()) == sampleRect.GetScalar(leftDir)) { retractLeft = true; } } if (!retractRight) { if (collidedRect.GetScalar(rightDir.Reverse()) == sampleRect.GetScalar(rightDir)) { retractRight = true; } } } // retract the rectangle if (retractLeft) { sampleRect.Expand(leftDir, -1); } if (retractRight) { sampleRect.Expand(rightDir, -1); } // if the rectangle has been retracted too much, we can't go on if (sampleRect.Area <= 0) { return(null); } // check for border availability between start and end bool foundFrom = HasBorderOpening(genFrom, sampleRect, dir); bool foundTo = HasBorderOpening(genTo, sampleRect, dir.Reverse()); // return the expansion if one is found if (foundFrom && foundTo) { return(new ListPathTraversalNode(chosenFrom, chosenTo, sampleRect)); } else { return(null); } }
private protected static List <List <RoomHallIndex> > GetBranchArms(FloorPlan floorPlan) { List <ListPathTraversalNode> endBranches = new List <ListPathTraversalNode>(); for (int ii = 0; ii < floorPlan.RoomCount; ii++) { FloorRoomPlan roomPlan = floorPlan.GetRoomPlan(ii); if (roomPlan.Adjacents.Count == 1) { endBranches.Add(new ListPathTraversalNode(new RoomHallIndex(-1, false), new RoomHallIndex(ii, false))); } } List <List <RoomHallIndex> > branchArms = new List <List <RoomHallIndex> >(); for (int nn = 0; nn < endBranches.Count; nn++) { ListPathTraversalNode chosenBranch = endBranches[nn]; List <RoomHallIndex> arm = new List <RoomHallIndex>(); while (true) { List <RoomHallIndex> connectors = new List <RoomHallIndex>(); List <RoomHallIndex> adjacents = floorPlan.GetRoomHall(chosenBranch.To).Adjacents; foreach (RoomHallIndex dest in adjacents) { if (dest != chosenBranch.From) { connectors.Add(dest); } } if (connectors.Count == 1) { arm.Add(chosenBranch.To); chosenBranch = new ListPathTraversalNode(chosenBranch.To, connectors[0]); } else if (connectors.Count == 0) { // we've reached the other side of a single line; add arm.Add(chosenBranch.To); // but also find the other pending arm and remove it for (int ii = endBranches.Count - 1; ii > nn; ii--) { ListPathTraversalNode otherBranch = endBranches[ii]; if (chosenBranch.To == otherBranch.To) { endBranches.RemoveAt(ii); } } // end the loop break; } else { break; } } branchArms.Add(arm); } return(branchArms); }
public override void ApplyToPath(IRandom rand, FloorPlan floorPlan) { List <List <RoomHallIndex> > candBranchPoints = GetBranchArms(floorPlan); // remove the rooms that do not pass filter for (int xx = 0; xx < candBranchPoints.Count; xx++) { for (int yy = candBranchPoints[xx].Count - 1; yy >= 0; yy--) { IFloorRoomPlan plan = floorPlan.GetRoomHall(candBranchPoints[xx][yy]); if (!BaseRoomFilter.PassesAllFilters(plan, this.Filters)) { candBranchPoints[xx].RemoveAt(yy); } } } // compute a goal amount of branches to connect // this computation ignores the fact that some terminals may be impossible var randBin = new RandBinomial(candBranchPoints.Count, this.ConnectPercent); int connectionsLeft = randBin.Pick(rand); while (candBranchPoints.Count > 0 && connectionsLeft > 0) { // choose random point to connect from int randIndex = rand.Next(candBranchPoints.Count); var chosenDestResult = ChooseConnection(rand, floorPlan, candBranchPoints[randIndex]); if (chosenDestResult is ListPathTraversalNode chosenDest) { // connect PermissiveRoomGen <T> hall = (PermissiveRoomGen <T>) this.GenericHalls.Pick(rand).Copy(); hall.PrepareSize(rand, chosenDest.Connector.Size); hall.SetLoc(chosenDest.Connector.Start); floorPlan.AddHall(hall, this.Components.Clone(), chosenDest.From, chosenDest.To); candBranchPoints.RemoveAt(randIndex); connectionsLeft--; GenContextDebug.DebugProgress("Added Connection"); // check to see if connection destination was also a candidate, // counting this as a double if so for (int ii = candBranchPoints.Count - 1; ii >= 0; ii--) { for (int jj = 0; jj < candBranchPoints[ii].Count; jj++) { if (candBranchPoints[ii][jj] == chosenDest.To) { candBranchPoints.RemoveAt(ii); connectionsLeft--; break; } } } } else { // remove the list anyway, but don't call it a success candBranchPoints.RemoveAt(randIndex); } } }