public virtual IFloorRoomPlan GetRoomHall(RoomHallIndex room) { if (!room.IsHall) { return(this.Rooms[room.Index]); } else { return(this.Halls[room.Index]); } }
public void EraseRoomHall(RoomHallIndex roomHall) { if (!roomHall.IsHall) { this.Rooms.RemoveAt(roomHall.Index); } else { this.Halls.RemoveAt(roomHall.Index); } // go through the rest of the rooms, removing the removed listroomhall from adjacents // also correcting their indices foreach (var plan in this.Rooms) { for (int jj = plan.Adjacents.Count - 1; jj >= 0; jj--) { RoomHallIndex adj = plan.Adjacents[jj]; if (adj.IsHall == roomHall.IsHall) { if (adj.Index == roomHall.Index) { plan.Adjacents.RemoveAt(jj); } else if (adj.Index > roomHall.Index) { plan.Adjacents[jj] = new RoomHallIndex(adj.Index - 1, adj.IsHall); } } } } foreach (var plan in this.Halls) { for (int jj = plan.Adjacents.Count - 1; jj >= 0; jj--) { RoomHallIndex adj = plan.Adjacents[jj]; if (adj.IsHall == roomHall.IsHall) { if (adj.Index == roomHall.Index) { plan.Adjacents.RemoveAt(jj); } else if (adj.Index > roomHall.Index) { plan.Adjacents[jj] = new RoomHallIndex(adj.Index - 1, adj.IsHall); } } } } }
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 virtual bool SpawnInRoom(TGenContext map, RoomHallIndex roomIndex, TSpawnable spawn) { IRoomGen room = map.RoomPlan.GetRoomHall(roomIndex).RoomGen; List <Loc> freeTiles = map.GetFreeTiles(room.Draw); if (freeTiles.Count > 0) { int randIndex = map.Rand.Next(freeTiles.Count); map.PlaceItem(freeTiles[randIndex], spawn); return(true); } return(false); }
public virtual List <int> GetAdjacentRooms(int roomIndex) { RoomHallIndex fullIndex = new RoomHallIndex(roomIndex, false); // skips halls // every listroomplan keeps a list of adjacents for easy traversal // just because two rooms are next to each other doesn't mean they will be adjacents // their openings may not align and therefore have free reign to block the path off from each other // the rules of this generator only say that if you park two rooms next to each other, // you must prepare for the possibility that they become connected. // once again, the philosophy that some setups may be cheesable, // but all setups are completable. List <int> returnList = new List <int>(); void NodeAct(RoomHallIndex nodeIndex, int distance) { // only add nodes that are if (nodeIndex.IsHall) { return; // Not a hall node } if (nodeIndex == fullIndex) { return; // Not the start node } returnList.Add(nodeIndex.Index); } List <RoomHallIndex> GetAdjacents(RoomHallIndex nodeIndex) { // do not add adjacents if we arrive on a room // unless it's the first one. if (nodeIndex == fullIndex) { return(this.Rooms[roomIndex].Adjacents); } else if (nodeIndex.IsHall) { return(this.Halls[nodeIndex.Index].Adjacents); } return(new List <RoomHallIndex>()); } Graph.TraverseBreadthFirst(fullIndex, NodeAct, GetAdjacents); return(returnList); }
public int GetDistance(RoomHallIndex roomFrom, RoomHallIndex roomTo) { int returnValue = -1; void NodeAct(RoomHallIndex nodeIndex, int distance) { if (nodeIndex == roomTo) { returnValue = distance; } } Graph.TraverseBreadthFirst(roomFrom, NodeAct, this.GetAdjacents); return(returnValue); }
public void TransferBorderToAdjacents(RoomHallIndex from) { IFloorRoomPlan basePlan = this.GetRoomHall(from); IRoomGen roomGen = basePlan.RoomGen; List <RoomHallIndex> adjacents = basePlan.Adjacents; foreach (RoomHallIndex adjacent in adjacents) { // first determine if this adjacent should be receiving info if ((!from.IsHall && adjacent.IsHall) || (from.IsHall == adjacent.IsHall && from.Index < adjacent.Index)) { IRoomGen adjacentGen = this.GetRoomHall(adjacent).RoomGen; adjacentGen.ReceiveOpenedBorder(roomGen, GetDirAdjacent(adjacentGen, roomGen)); } } }
public int GetDistance(RoomHallIndex roomFrom, RoomHallIndex roomTo) { int returnValue = -1; int startIndex = roomFrom.Index + (roomFrom.IsHall ? this.Rooms.Count : 0); int endIndex = roomTo.Index + (roomTo.IsHall ? this.Rooms.Count : 0); void NodeAct(int nodeIndex, int distance) { if (nodeIndex == endIndex) { returnValue = distance; } } Graph.TraverseBreadthFirst(this.Rooms.Count + this.Halls.Count, startIndex, NodeAct, this.GetBreadthFirstAdjacents); return(returnValue); }
public bool IsChokePoint(RoomHallIndex room) { int roomsHit = 0; int hallsHit = 0; void NodeAct(RoomHallIndex nodeIndex, int distance) { if (!nodeIndex.IsHall) { roomsHit++; } else { hallsHit++; } } Graph.TraverseBreadthFirst(room, NodeAct, this.GetAdjacents); int totalRooms = roomsHit; int totalHalls = hallsHit; roomsHit = 0; hallsHit = 0; if (!room.IsHall) { roomsHit++; } else { hallsHit++; } List <RoomHallIndex> GetChokeAdjacents(RoomHallIndex nodeIndex) { List <RoomHallIndex> adjacents = new List <RoomHallIndex>(); List <RoomHallIndex> roomAdjacents = this.GetRoomHall(nodeIndex).Adjacents; // do not add adjacents if we arrive on a room // unless it's the first one. foreach (RoomHallIndex adjacentRoom in roomAdjacents) { // do not count the origin room if (adjacentRoom == room) { continue; } adjacents.Add(adjacentRoom); } return(adjacents); } IFloorRoomPlan plan = this.GetRoomHall(room); if (plan.Adjacents.Count > 0) { Graph.TraverseBreadthFirst(plan.Adjacents[0], NodeAct, GetChokeAdjacents); } return((roomsHit != totalRooms) || (hallsHit != totalHalls)); }
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 ListPathBranchExpansion(RoomHallIndex from, IRoomGen room, IPermissiveRoomGen hall) { this.From = from; this.Room = room; this.Hall = hall; }
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; } }
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); IRoomPlan oldPlan = floorPlan.GetRoomHall(oldRoomHall); // 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) == oldPlan.RoomGen.Draw.GetScalar(dir)) { newAdjacents.AddRange(adjacentsByDir[dir]); } else if (adjacentsByDir[dir].Count > 0) { Rect supportRect = GetSupportRect(floorPlan, oldPlan.RoomGen, 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); ComponentCollection newCollection = oldPlan.Components.Clone(); foreach (RoomComponent component in this.RoomComponents) { newCollection.Set(component.Clone()); } floorPlan.AddRoom(newGen, newCollection, 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]); ComponentCollection newHallCollection = oldPlan.Components.Clone(); foreach (RoomComponent component in this.HallComponents) { newHallCollection.Set(component.Clone()); } floorPlan.AddHall(supportHalls[dir], newHallCollection.Clone(), adjToAdd.ToArray()); } } }
/// <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); }
public virtual List <RoomHallIndex> GetAdjacents(RoomHallIndex nodeIndex) { return(this.GetRoomHall(nodeIndex).Adjacents); }
public ListPathTraversalNode(RoomHallIndex from, RoomHallIndex to) { this.From = from; this.To = to; this.Connector = Rect.Empty; }
public bool IsChokePoint(RoomHallIndex room) { int roomsHit = 0; int hallsHit = 0; void NodeAct(int nodeIndex, int distance) { if (nodeIndex < this.Rooms.Count) { roomsHit++; } else { hallsHit++; } } int startIndex = room.Index + (room.IsHall ? this.Rooms.Count : 0); Graph.TraverseBreadthFirst(this.Rooms.Count + this.Halls.Count, startIndex, NodeAct, this.GetBreadthFirstAdjacents); int totalRooms = roomsHit; int totalHalls = hallsHit; roomsHit = 0; hallsHit = 0; if (!room.IsHall) { roomsHit++; } else { hallsHit++; } List <int> GetChokeAdjacents(int nodeIndex) { List <int> adjacents = new List <int>(); List <RoomHallIndex> roomAdjacents = new List <RoomHallIndex>(); // do not add adjacents if we arrive on a room // unless it's the first one. if (nodeIndex < this.Rooms.Count) { roomAdjacents = this.Rooms[nodeIndex].Adjacents; } else { roomAdjacents = this.Halls[nodeIndex - this.Rooms.Count].Adjacents; } foreach (RoomHallIndex adjacentRoom in roomAdjacents) { // do not count the origin room if (adjacentRoom == room) { continue; } if (adjacentRoom.IsHall) { adjacents.Add(adjacentRoom.Index + this.Rooms.Count); } else { adjacents.Add(adjacentRoom.Index); } } return(adjacents); } IFloorRoomPlan plan = this.GetRoomHall(room); if (plan.Adjacents.Count > 0) { int adjIndex = plan.Adjacents[0].Index + (plan.Adjacents[0].IsHall ? this.Rooms.Count : 0); Graph.TraverseBreadthFirst(this.Rooms.Count + this.Halls.Count, adjIndex, NodeAct, GetChokeAdjacents); } return((roomsHit != totalRooms) || (hallsHit != totalHalls)); }
public ListPathTraversalNode(RoomHallIndex from, RoomHallIndex to, Rect connector) { this.From = from; this.To = to; this.Connector = connector; }
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); } }