public CorridorTile ChooseNextTile(Dungeon dungeon, double chanceToTurn) { Direction nextDirection = ChooseNextDirection(chanceToTurn); Tile nextTile = dungeon.GetTileByDirection(Tile, nextDirection); return(new CorridorTile(nextTile, this, nextDirection)); }
/// <summary> /// Account for all the cases a door may open into. /// - Another room. Carve no corridor, just link areas /// - A wall of another room. Carve a corridor straight ahead until a walkable space is found /// - Solid rock. Call the flood-fill search to find a door or path for a corridor to connect to /// </summary> /// <param name="door"></param> /// <param name="chanceToTurn"></param> /// <param name="allowConnectionToConnectedArea"></param> public void GenerateCorridor(Tile door, double chanceToTurn, bool allowConnectionToConnectedArea) { Tile startOfPath = Dungeon.GetTileByDirection(door, door.Direction); // If door is already connected to a path or another door, there is nothing to do // (The initial set of doors touch no other doors, so if a door is adjacent, // it was spawned while carving a path straight ahead.) if (Dungeon.IsTileAdjacentTo(door, Space.WALKABLE, Dungeon.GetTileByDirection(door, Tile.Invert(door.Direction)))) { if (allowConnectionToConnectedArea || !door.Area.To.Contains(startOfPath.Area)) { ConnectAreas(door); } else { EraseDoor(door); } return; } // If the door has opened into a wall, carve straight ahead until the other room can be entered // Then exit this method call if (startOfPath.Space == Space.Wall) { CorridorThroughRoomWall(door, startOfPath, allowConnectionToConnectedArea); return; } // If there is solid stone ahead, start a path CorridorWalk(door, chanceToTurn, allowConnectionToConnectedArea); }
/// <summary> /// Randomly chooses a wall tile as a door. /// Does not choose tiles touching an existing door, not even by a corner. /// </summary> /// <returns></returns> public Tile GenerateDoor() { int tries = 0; while (tries < 100) { // Because rooms can share walls, some wall tiles may already be doors from another room // So check the actual tile as well as this room's list of doors Tile tile = walls[DungeonGenerator.Rng.Next(0, walls.Count)]; if (!Doors.Contains(tile) && tile.Space != Space.Door && !Dungeon.IsTileSurroundedBy(tile, Space.Door) && Dungeon.GetTileByDirection(tile).Space != Space.Granite) { SetTileAsDoor(tile); return(tile); } ++tries; } foreach (Tile tile in walls) { tile.Debug = true; } return(null); //throw new InvalidOperationException("Room for another door could not be found"); }
/// <summary> /// If a door opens into the wall of another room, carving straight ahead is guaranteed /// to open into that room. Make a door at the other end. /// It's possible that if the door opens into the corner of another room, our corridor /// opens into another corridor first. This is fine, just don't make the last tile a door. /// </summary> /// <param name="door"></param> /// <param name="startOfPath"></param> public void CorridorThroughRoomWall(Tile door, Tile startOfPath, bool allowConnectionToConnectedArea) { // Prepare a stack of path tiles so we can call our general method to carve the corridor Stack <CorridorTile> path = new Stack <CorridorTile>(); CorridorTile wrappedDoor = new CorridorTile(door, null, door.Direction); CorridorTile head = new CorridorTile(startOfPath, wrappedDoor, door.Direction); path.Push(head); while (!Dungeon.IsTileAdjacentTo(head.Tile, Space.WALKABLE, head.From.Tile)) { Tile nextTile = Dungeon.GetTileByDirection(head.Tile, door.Direction); head = new CorridorTile(nextTile, head, door.Direction); path.Push(head); } if (!allowConnectionToConnectedArea && !RoomUnconnectedToAdjacentArea(door.Area, Dungeon.GetAdjacentTilesOfType(head.Tile, Space.WALKABLE, head.From.Tile))) { EraseDoor(door); return; } // If the last tile is directly touching another room's interior, make this tile a door // As a wall tile originally, the door tile is already facing out from its room Tile otherDoor = path.Peek().Tile; if (Dungeon.IsTileAdjacentTo(otherDoor, Space.Room)) { path.Pop(); Room otherRoom = (Room)otherDoor.Area; otherRoom.SetTileAsDoor(otherDoor); } // If we didn't pop our only path tile off of the stack, carve the remaining corridor if (path.Count > 0) { CarveCorridor(path); } else { ConnectAreas(door); } }
/// <summary> /// Perform a flood-fill search from a door to another room's door or a path. /// </summary> /// <param name="door"></param> /// <param name="chanceToTurn"></param> public void CorridorWalk(Tile door, double chanceToTurn, bool allowConnectionToConnectedArea) { bool DoorLeadsToOtherRoom(List <Tile> doors) { foreach (Tile d in doors) { if (door.Area != d.Area) { return(true); } } return(false); } HashSet <CorridorTile> visited = new HashSet <CorridorTile>(); Stack <CorridorTile> path = new Stack <CorridorTile>(); Tile firstTile = Dungeon.GetTileByDirection(door); CorridorTile start = new CorridorTile(door, null, door.Direction); CorridorTile head = new CorridorTile(firstTile, start, door.Direction); path.Push(head); visited.Add(head); while (path.Count > 0) { head = path.Peek(); // Can we carve this tile? // If a door is on the head of the stack, it belongs to the room we came from. // (If we had found a door to another room, we would already have exited the while loop) // Treat these doors as walls. if (head.Space == Space.Wall || head.Space == Space.Granite || head.Space == Space.Door) { path.Pop(); continue; } // Have we found any doors? // If all adjacent doors lead to the room we came from, carry on if (Dungeon.IsTileAdjacentTo(head.Tile, Space.Door, head.From.Tile) && DoorLeadsToOtherRoom(Dungeon.GetAdjacentTilesOfType(head.Tile, Space.Door, head.From.Tile))) { CarveCorridor(path); return; } // Have we found an existing path? Connect to it and end // If we are not allowed to connect to it, then we must skirt around it if (Dungeon.IsTileAdjacentTo(head.Tile, Space.Path, head.From.Tile)) { if (allowConnectionToConnectedArea || RoomUnconnectedToAdjacentArea(door.Area, Dungeon.GetAdjacentTilesOfType(head.Tile, Space.Path, head.From.Tile))) { CarveCorridor(path); return; } else { // Do not allow our path to touch existing paths. Treat this tile as non-carvable path.Pop(); continue; } } // Decide where to go next, or step back one tile if all paths have been explored if (head.DirectionsToTry.Count > 0) { CorridorTile next; do { next = head.ChooseNextTile(Dungeon, chanceToTurn); } while (visited.Contains(next) && head.DirectionsToTry.Count > 0); if (visited.Contains(next)) { path.Pop(); } else { path.Push(next); visited.Add(next); } } else { path.Pop(); } } // There are no doors or paths to connect to, so erase this door EraseDoor(door); }