public void AddHall(IPermissiveRoomGen gen, ComponentCollection components, params RoomHallIndex[] attached) { // we expect that the hall has already been given a size... // check against colliding on other rooms (and not halls) foreach (var room in this.Rooms) { if (Collision.Collides(room.RoomGen.Draw, gen.Draw)) { throw new InvalidOperationException("Tried to add on top of an existing room!"); } } // check against rooms that go out of bounds if (!this.DrawRect.Contains(gen.Draw)) { throw new InvalidOperationException("Tried to add out of range!"); } var plan = new FloorHallPlan(gen, components); // attach everything plan.Adjacents.AddRange(attached); foreach (RoomHallIndex fromRoom in attached) { IFloorRoomPlan fromPlan = this.GetRoomHall(fromRoom); fromPlan.Adjacents.Add(new RoomHallIndex(this.Halls.Count, true)); } this.Halls.Add(plan); }
private static void InitFloorToContext(TestFloorPlan floorPlan, Rect rect, Rect[] rooms, Rect[] halls, Tuple <char, char>[] links) { floorPlan.InitRect(rect); // a quick way to set up rooms, halls, and connections // a list of rects for rooms, a list of rects for halls for (int ii = 0; ii < rooms.Length; ii++) { var gen = new TestFloorPlanGen((char)('A' + ii)); gen.PrepareDraw(rooms[ii]); floorPlan.PublicRooms.Add(new FloorRoomPlan(gen, new ComponentCollection())); } for (int ii = 0; ii < halls.Length; ii++) { var gen = new TestFloorPlanGen((char)('a' + ii)); gen.PrepareDraw(halls[ii]); floorPlan.PublicHalls.Add(new FloorHallPlan(gen, new ComponentCollection())); } // and finally a list of tuples that link rooms to rooms and halls to halls for (int ii = 0; ii < links.Length; ii++) { bool hall1 = links[ii].Item1 >= 'a'; int index1 = hall1 ? links[ii].Item1 - 'a' : links[ii].Item1 - 'A'; bool hall2 = links[ii].Item2 >= 'a'; int index2 = hall2 ? links[ii].Item2 - 'a' : links[ii].Item2 - 'A'; var link1 = new RoomHallIndex(index1, hall1); var link2 = new RoomHallIndex(index2, hall2); IFloorRoomPlan from1 = floorPlan.GetRoomHall(link1); IFloorRoomPlan from2 = floorPlan.GetRoomHall(link2); from1.Adjacents.Add(link2); from2.Adjacents.Add(link1); } }
public override void Apply(T map) { //Iterate every room/hall and coat the ones filtered List <RoomHallIndex> spawningRooms = new List <RoomHallIndex>(); Dictionary <Loc, SealType> sealList = new Dictionary <Loc, SealType>(); for (int ii = 0; ii < map.RoomPlan.RoomCount; ii++) { if (!BaseRoomFilter.PassesAllFilters(map.RoomPlan.GetRoomPlan(ii), this.Filters)) { continue; } spawningRooms.Add(new RoomHallIndex(ii, false)); } for (int ii = 0; ii < map.RoomPlan.HallCount; ii++) { if (!BaseRoomFilter.PassesAllFilters(map.RoomPlan.GetHallPlan(ii), this.Filters)) { continue; } spawningRooms.Add(new RoomHallIndex(ii, true)); } if (spawningRooms.Count == 0) { return; } for (int ii = 0; ii < spawningRooms.Count; ii++) { IFloorRoomPlan plan = map.RoomPlan.GetRoomHall(spawningRooms[ii]); //seal the sides and note edge cases for (int xx = plan.RoomGen.Draw.X + 1; xx < plan.RoomGen.Draw.End.X - 1; xx++) { sealBorderRay(map, sealList, plan, new LocRay8(xx, plan.RoomGen.Draw.Y, Dir8.Up), Dir8.Left, Dir8.Right); sealBorderRay(map, sealList, plan, new LocRay8(xx, plan.RoomGen.Draw.End.Y - 1, Dir8.Down), Dir8.Left, Dir8.Right); } for (int yy = plan.RoomGen.Draw.Y + 1; yy < plan.RoomGen.Draw.End.Y - 1; yy++) { sealBorderRay(map, sealList, plan, new LocRay8(plan.RoomGen.Draw.X, yy, Dir8.Left), Dir8.Up, Dir8.Down); sealBorderRay(map, sealList, plan, new LocRay8(plan.RoomGen.Draw.End.X - 1, yy, Dir8.Right), Dir8.Up, Dir8.Down); } //seal edge cases sealCornerRay(map, sealList, plan, new LocRay8(plan.RoomGen.Draw.X, plan.RoomGen.Draw.Y, Dir8.UpLeft)); sealCornerRay(map, sealList, plan, new LocRay8(plan.RoomGen.Draw.End.X - 1, plan.RoomGen.Draw.Y, Dir8.UpRight)); sealCornerRay(map, sealList, plan, new LocRay8(plan.RoomGen.Draw.X, plan.RoomGen.Draw.End.Y - 1, Dir8.DownLeft)); sealCornerRay(map, sealList, plan, new LocRay8(plan.RoomGen.Draw.End.X - 1, plan.RoomGen.Draw.End.Y - 1, Dir8.DownRight)); } PlaceBorders(map, sealList); }
public override void DistributeSpawns(TGenContext map, List <TSpawnable> spawns) { // gather up all rooms and put in a spawn list // rooms that are farther from the start are more likely to have items var spawningRooms = new SpawnList <RoomHallIndex>(); Dictionary <RoomHallIndex, int> roomWeights = new Dictionary <RoomHallIndex, int>(); // get the start room int startRoom = 0; for (int ii = 0; ii < map.RoomPlan.RoomCount; ii++) { FloorRoomPlan room = map.RoomPlan.GetRoomPlan(ii); if (Collision.InBounds(room.RoomGen.Draw, map.GetLoc(0))) { startRoom = ii; break; } } int maxVal = 1; void NodeAct(RoomHallIndex nodeIndex, int distance) { roomWeights[nodeIndex] = distance + 1; maxVal = Math.Max(maxVal, roomWeights[nodeIndex]); } Graph.TraverseBreadthFirst(new RoomHallIndex(startRoom, false), NodeAct, map.RoomPlan.GetAdjacents); int multFactor = int.MaxValue / maxVal / roomWeights.Count; foreach (RoomHallIndex idx in roomWeights.Keys) { IFloorRoomPlan room = map.RoomPlan.GetRoomHall(idx); if (idx.IsHall && !this.IncludeHalls) { continue; } if (!BaseRoomFilter.PassesAllFilters(room, this.Filters)) { continue; } if (roomWeights[idx] == 0) { continue; } spawningRooms.Add(idx, roomWeights[idx] * multFactor); } this.SpawnRandInCandRooms(map, spawningRooms, spawns, this.SuccessPercent); }
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 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 void DrawOnMap(ITiledGenContext map) { GenContextDebug.StepIn("Main Rooms"); for (int ii = 0; ii < this.Rooms.Count; ii++) { // take in the broad fulfillables from adjacent rooms that have not yet drawn IFloorRoomPlan plan = this.Rooms[ii]; foreach (RoomHallIndex adj in plan.Adjacents) { if (adj.IsHall || adj.Index > ii) { IRoomGen adjacentGen = this.GetRoomHall(adj).RoomGen; plan.RoomGen.ReceiveFulfillableBorder(adjacentGen, GetDirAdjacent(plan.RoomGen, adjacentGen)); } } plan.RoomGen.DrawOnMap(map); this.TransferBorderToAdjacents(new RoomHallIndex(ii, false)); GenContextDebug.DebugProgress("Draw Room"); } GenContextDebug.StepOut(); GenContextDebug.StepIn("Connecting Halls"); for (int ii = 0; ii < this.Halls.Count; ii++) { // take in the broad fulfillables from adjacent rooms that have not yet drawn IFloorRoomPlan plan = this.Halls[ii]; foreach (RoomHallIndex adj in plan.Adjacents) { if (adj.IsHall && adj.Index > ii) { IRoomGen adjacentGen = this.GetRoomHall(adj).RoomGen; plan.RoomGen.ReceiveFulfillableBorder(adjacentGen, GetDirAdjacent(plan.RoomGen, adjacentGen)); } } plan.RoomGen.DrawOnMap(map); this.TransferBorderToAdjacents(new RoomHallIndex(ii, true)); GenContextDebug.DebugProgress("Draw Hall"); } GenContextDebug.StepOut(); }
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 void AddRoom(IRoomGen gen, ComponentCollection components, params RoomHallIndex[] attached) { // check against colliding on other rooms (and not halls) foreach (var room in this.Rooms) { if (Collision.Collides(room.RoomGen.Draw, gen.Draw)) { throw new InvalidOperationException("Tried to add on top of an existing room!"); } } foreach (var hall in this.Halls) { if (Collision.Collides(hall.RoomGen.Draw, gen.Draw)) { throw new InvalidOperationException("Tried to add on top of an existing hall!"); } } // check against rooms that go out of bounds if (!this.DrawRect.Contains(gen.Draw)) { throw new InvalidOperationException("Tried to add out of range!"); } // we expect that the room has already been given a size // and that its fulfillables match up with its adjacent's fulfillables. var plan = new FloorRoomPlan(gen, components); // attach everything plan.Adjacents.AddRange(attached); foreach (RoomHallIndex fromRoom in attached) { IFloorRoomPlan fromPlan = this.GetRoomHall(fromRoom); fromPlan.Adjacents.Add(new RoomHallIndex(this.Rooms.Count, false)); } this.Rooms.Add(plan); }
private void sealCornerRay(T map, Dictionary <Loc, SealType> sealList, IFloorRoomPlan plan, LocRay8 locRay) { DirH dirH; DirV dirV; locRay.Dir.Separate(out dirH, out dirV); bool outwardsH = sealBorderRay(map, sealList, plan, new LocRay8(locRay.Loc, dirH.ToDir8()), dirV.ToDir8().Reverse(), Dir8.None); bool outwardsV = sealBorderRay(map, sealList, plan, new LocRay8(locRay.Loc, dirV.ToDir8()), dirH.ToDir8().Reverse(), Dir8.None); //when two directions of a corner tile face inward, or outward, or a combination of inward and outward //-both inward: needs to not be redundant across the two sides - handled by hashset, no action needed //-one inward and one outward: can coexist - no action needed //-both outward: needs to check the outward diagonal to see if it forces inward // -if it doesnt force inward, do an outward operation // -if it does, do an inward operation if (outwardsH && outwardsV) { sealBorderRay(map, sealList, plan, locRay, Dir8.None, Dir8.None); } }
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); } }
/// <summary> /// chooses and caegorizes the tile to be sealed /// </summary> /// <param name="map"></param> /// <param name="sealList"></param> /// <param name="plan"></param> /// <param name="loc"></param> /// <param name="dir"></param> /// <returns>Whether it affected the tile outwards or not</returns> private bool sealBorderRay(T map, Dictionary <Loc, SealType> sealList, IFloorRoomPlan plan, LocRay8 locRay, Dir8 side1, Dir8 side2) { Loc forthLoc = locRay.Loc + locRay.Dir.GetLoc(); bool hasAdjacent = false; bool hasCondition = false; for (int ii = 0; ii < plan.Adjacents.Count; ii++) { IFloorRoomPlan adjacentPlan = map.RoomPlan.GetRoomHall(plan.Adjacents[ii]); if (Collision.InBounds(adjacentPlan.RoomGen.Draw, forthLoc)) { hasAdjacent = true; if (BaseRoomFilter.PassesAllFilters(adjacentPlan, this.Filters)) { hasCondition = true; break; } } } if (!hasAdjacent) { //in the case where the extending tile is within no adjacents // all normal walls shall be turned into impassables // everything else is saved into the lock list sealBorderTile(map, sealList, SealType.Locked, forthLoc); return(true); } else if (!hasCondition) { //in the case where the extending tile is within an adjacent and that adjacent DOESNT pass filter // all normal walls for the INWARD border shall be turned into impassables // everything else for the INWARD border shall be saved into a key list if (map.Tiles[forthLoc.X][forthLoc.Y].TileEquivalent(map.RoomTerrain)) { sealBorderTile(map, sealList, SealType.Key, locRay.Loc); } else { sealBorderTile(map, sealList, SealType.Locked, locRay.Loc); } //when transitioning between inward and outward //-when transitioning from outward to inward, the previous outward tile needs an inward check //-when transitioning from inward to outward, the current outward tile needs a inward check //in the interest of trading redundancy for simplicity, an inward block will just block the tiles to the sides //regardless of if they've already been blocked //redundancy will be handled by hashsets if (side1 != Dir8.None) { Loc sideLoc = locRay.Loc + side1.GetLoc(); sealBorderTile(map, sealList, SealType.Locked, sideLoc); } if (side2 != Dir8.None) { Loc sideLoc = locRay.Loc + side2.GetLoc(); sealBorderTile(map, sealList, SealType.Locked, sideLoc); } return(false); } else { //in the case where the extending tile is within an adjacent and that adjacent passes filter // do nothing and skip these tiles return(true); } }
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)); }
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 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); } } }