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 override void ApplyToPath(IRandom rand, GridPlan floorPlan) { if (floorPlan.GridWidth < 3 || floorPlan.GridHeight < 3) { throw new InvalidOperationException("Not enough room to create path."); } int maxRooms = (2 * floorPlan.GridWidth) + (2 * floorPlan.GridHeight) - 4; int roomOpen = maxRooms * this.CircleRoomRatio.Pick(rand) / 100; int paths = this.Paths.Pick(rand); if (roomOpen < 1 && paths < 1) { roomOpen = 1; } GenContextDebug.StepIn("Outer Circle"); // draw the top and bottom for (int xx = 0; xx < floorPlan.GridWidth; xx++) { this.RollOpenRoom(rand, floorPlan, new Loc(xx, 0), ref roomOpen, ref maxRooms); GenContextDebug.DebugProgress("Room"); this.RollOpenRoom(rand, floorPlan, new Loc(xx, floorPlan.GridHeight - 1), ref roomOpen, ref maxRooms); GenContextDebug.DebugProgress("Room"); if (xx > 0) { floorPlan.SetHall(new LocRay4(new Loc(xx, 0), Dir4.Left), this.GenericHalls.Pick(rand), this.HallComponents.Clone()); GenContextDebug.DebugProgress("Hall"); floorPlan.SetHall(new LocRay4(new Loc(xx, floorPlan.GridHeight - 1), Dir4.Left), this.GenericHalls.Pick(rand), this.HallComponents.Clone()); GenContextDebug.DebugProgress("Hall"); } } // draw the left and right (excluding the top and bottom) for (int yy = 0; yy < floorPlan.GridHeight; yy++) { // exclude the top and bottom if (yy > 0 && yy < floorPlan.GridHeight - 1) { this.RollOpenRoom(rand, floorPlan, new Loc(0, yy), ref roomOpen, ref maxRooms); GenContextDebug.DebugProgress("Room"); this.RollOpenRoom(rand, floorPlan, new Loc(floorPlan.GridWidth - 1, yy), ref roomOpen, ref maxRooms); GenContextDebug.DebugProgress("Room"); } if (yy > 0) { floorPlan.SetHall(new LocRay4(new Loc(0, yy), Dir4.Up), this.GenericHalls.Pick(rand), this.HallComponents.Clone()); GenContextDebug.DebugProgress("Hall"); floorPlan.SetHall(new LocRay4(new Loc(floorPlan.GridWidth - 1, yy), Dir4.Up), this.GenericHalls.Pick(rand), this.HallComponents.Clone()); GenContextDebug.DebugProgress("Hall"); } } GenContextDebug.StepOut(); GenContextDebug.StepIn("Inner Paths"); Rect innerRect = new Rect(1, 1, floorPlan.GridWidth - 2, floorPlan.GridHeight - 2); // create inner paths for (int pathsMade = 0; pathsMade < paths; pathsMade++) { GenContextDebug.StepIn($"Path {pathsMade}"); Dir4 startDir = DirExt.VALID_DIR4.ElementAt(rand.Next(DirExt.DIR4_COUNT)); int x = rand.Next(innerRect.Start.X, innerRect.End.X); int y = rand.Next(innerRect.Start.Y, innerRect.End.Y); switch (startDir) { case Dir4.Down: y = 0; break; case Dir4.Left: x = floorPlan.GridWidth - 1; break; case Dir4.Up: y = floorPlan.GridHeight - 1; break; case Dir4.Right: x = 0; break; case Dir4.None: break; default: throw new ArgumentOutOfRangeException(nameof(startDir), "Invalid enum value."); } Loc wanderer = new Loc(x, y); Dir4 prevDir = Dir4.None; // direction of movement int pathLength = (startDir.ToAxis() == Axis4.Vert) ? innerRect.Height : innerRect.Width; for (int currentLength = 0; currentLength < pathLength; currentLength++) { Dir4 chosenDir = startDir; // avoid this block the first time if (currentLength > 0) { List <Dir4> dirs = new List <Dir4>(); foreach (Dir4 dir in DirExt.VALID_DIR4) { // do not backtrack if (dir == prevDir) { continue; } // do not hit edge if (!Collision.InBounds(innerRect, wanderer + dir.GetLoc())) { continue; } dirs.Add(dir); } chosenDir = dirs[rand.Next(dirs.Count)]; } Loc dest = wanderer + chosenDir.GetLoc(); GridRoomPlan existingRoom = floorPlan.GetRoomPlan(dest); if (existingRoom == null) { if (currentLength == pathLength - 1) // only the end room can be a non-hall { floorPlan.AddRoom(dest, this.GenericRooms.Pick(rand), this.RoomComponents.Clone()); } else { floorPlan.AddRoom(dest, this.GetDefaultGen(), this.HallComponents.Clone(), true); } GenContextDebug.DebugProgress("Room"); } else if (existingRoom.PreferHall) { if (currentLength == pathLength - 1) { // override the hall room existingRoom.RoomGen = this.GenericRooms.Pick(rand).Copy(); existingRoom.PreferHall = false; } } floorPlan.SetHall(new LocRay4(wanderer, chosenDir), this.GenericHalls.Pick(rand), this.HallComponents.Clone()); GenContextDebug.DebugProgress("Hall"); wanderer = dest; prevDir = chosenDir.Reverse(); } GenContextDebug.StepOut(); } GenContextDebug.StepOut(); }
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); } }
/// <summary> /// In a 4- or 3-way hall situation, this method is called to add the remaining ways after the first two have been added. /// </summary> /// <param name="map"></param> /// <param name="cross"></param> /// <param name="possibleStarts"></param> /// <param name="vertical"></param> /// <param name="turn"></param> private void DrawSecondaryHall(T map, HashSet <int> cross, Dictionary <Dir4, List <HashSet <int> > > possibleStarts, bool vertical, bool turn) { if (!turn) { // if not turning, use the cross variables this.DrawStraightHall(map, cross, vertical); } else { Dir4 forwardDir = vertical ? Dir4.Up : Dir4.Left; List <HashSet <int> > starts = possibleStarts[forwardDir]; List <HashSet <int> > ends = possibleStarts[forwardDir.Reverse()]; // the chosen tiles to start digging the hall from int[] startTiles = new int[starts.Count]; int[] endTiles = new int[ends.Count]; if (starts.Count == 1 && ends.Count == 1) { Choose1on1BentHallStarts(map, starts[0], ends[0], startTiles, endTiles); // forward until hit { Loc forwardStart = this.Draw.Start; Loc startLoc = new Loc(vertical ? startTiles[0] : forwardStart.X, vertical ? forwardStart.Y : startTiles[0]); Loc endLoc = startLoc; // the assumption is that there is already roomterrain to cross over at another point in this room while (!map.GetTile(endLoc).TileEquivalent(map.RoomTerrain)) { endLoc += forwardDir.Reverse().GetLoc(); } this.DrawHall(map, startLoc, endLoc, vertical); } // backward until hit { Loc backwardStart = this.Draw.End - new Loc(1); Loc startLoc = new Loc(vertical ? endTiles[0] : backwardStart.X, vertical ? backwardStart.Y : endTiles[0]); Loc endLoc = startLoc; // the assumption is that there is already roomterrain to cross over at another point in this room while (!map.GetTile(endLoc).TileEquivalent(map.RoomTerrain)) { endLoc += forwardDir.GetLoc(); } this.DrawHall(map, startLoc, endLoc, vertical); } } else { // if turning, use the respective possible starts and draw until the primary lines are hit foreach (Dir4 dir in DirExt.VALID_DIR4) { // choose vertical starts if vertical, horiz starts if otherwise if ((dir.ToAxis() == Axis4.Vert) == vertical) { for (int jj = 0; jj < possibleStarts[dir].Count; jj++) { int[] crossArray = new int[possibleStarts[dir][jj].Count]; possibleStarts[dir][jj].CopyTo(crossArray); int startSideDist = crossArray[map.Rand.Next(crossArray.Length)]; Loc forwardStart = (dir == Dir4.Up || dir == Dir4.Left) ? this.Draw.Start : this.Draw.End - new Loc(1); Loc startLoc = new Loc(vertical ? startSideDist : forwardStart.X, vertical ? forwardStart.Y : startSideDist); Loc endLoc = startLoc; // the assumption is that there is already roomterrain to cross over at another point in this room while (!map.GetTile(endLoc).TileEquivalent(map.RoomTerrain)) { endLoc += dir.Reverse().GetLoc(); } this.DrawHall(map, startLoc, endLoc, vertical); } } } // there is no guarantee that both sides will have an open bordertile; they'll just come in from their respective directions } } }
private List <List <LocRay4> > findHallSets(GridPlan floorPlan, Rect rect, List <LocRay4> raysOut) { bool[] raysCovered = new bool[raysOut.Count]; List <List <LocRay4> > resultList = new List <List <LocRay4> >(); Graph.GetAdjacents <int> getAdj = (int nodeIndex) => { List <int> returnList = new List <int>(); Loc loc = new Loc(nodeIndex % floorPlan.GridWidth, nodeIndex / floorPlan.GridWidth); int roomIndex = floorPlan.GetRoomIndex(loc); for (int dd = 0; dd < DirExt.DIR4_COUNT; dd++) { Dir4 dir = (Dir4)dd; Loc destLoc = loc + dir.GetLoc(); //check against outside floor bound if (!Collision.InBounds(floorPlan.GridWidth, floorPlan.GridHeight, destLoc)) { continue; } //check against inside rect bound if (Collision.InBounds(rect, destLoc)) { continue; } //check against a valid room int destRoom = floorPlan.GetRoomIndex(destLoc); if (destRoom == roomIndex) { returnList.Add(destLoc.Y * floorPlan.GridWidth + destLoc.X); } else if (destRoom > -1) { if (floorPlan.GetHall(new LocRay4(loc, dir)) != null) { returnList.Add(destLoc.Y * floorPlan.GridWidth + destLoc.X); } } } return(returnList); }; //group the exits together for (int ii = 0; ii < raysOut.Count; ii++) { if (raysCovered[ii]) { continue; } Loc startLoc = raysOut[ii].Traverse(1); List <LocRay4> set = new List <LocRay4>(); Graph.TraverseBreadthFirst(startLoc.Y * floorPlan.GridWidth + startLoc.X, (int nodeIndex, int distance) => { Loc fillLoc = new Loc(nodeIndex % floorPlan.GridWidth, nodeIndex / floorPlan.GridWidth); for (int nn = 0; nn < raysOut.Count; nn++) { if (raysOut[nn].Traverse(1) == fillLoc) { set.Add(raysOut[nn]); raysCovered[nn] = true; } } }, getAdj); resultList.Add(set); } return(resultList); }