001package squidpony.squidgrid.mapping; 002 003import squidpony.squidgrid.mapping.styled.DungeonBoneGen; 004import squidpony.squidgrid.mapping.styled.TilesetType; 005import squidpony.squidmath.*; 006 007import java.util.ArrayList; 008 009/** 010 * An IDungeonGenerator that distorts and smooths an ordinary dungeon map to make it appear like a cave complex. 011 * This usually exhibits the complex connectivity that dungeons made with a {@link TilesetType} like 012 * {@link TilesetType#DEFAULT_DUNGEON} have, but shouldn't have noticeable room/corridor areas, and should appear as 013 * all one cave. 014 * <br> 015 * An example map this can produce: 016 * <br> 017 * {@code 018 * ┌─────┐ ┌───────┬─┐ ┌───┐ ┌────┐ ┌────┐ ┌────┐ ┌──┐ ┌───┐ ┌─────┐ 019 * ┌┘.....└─┘.......│.└─┘...└─┐ ┌─┐ ┌──┘....│ │....└──┬┘....└┬───┘..└──┐ ┌───┐ ┌┘...│ ┌┘.....└┐ 020 * │..........................└┐ ┌─┘.└┐ │.......└┐ │.......│......│.........└─┬┘...│ ┌───┬┘....│ │.......│ 021 * └┐.........┌┐...............└─┘....└┐ ┌┘........│ ┌┘....│.....................│...─┤┌─┘...│.....│ │.......│ 022 * │........┌┘│.......................└─┘.........└┐│.....│.....┌┐...................││...........│ │.......│ 023 * │........│┌┘...............................#....││.....│.....│└┐..................└┘...........│ │.......└┐ 024 * │.......┌┼┘.....┌┐............┌─┐..............┌┘│.....├─┐...│ │...............................│ │........│ 025 * │......┌┘│....┌─┘└┐.........┌─┘ └──────┐.......│ │....┌┘ │...└─┤......#.......│................│ └┐.......│ 026 * │.....┌┘ └┐.┌─┘ └┐.......┌┘ │......┌┘ │...┌┘ └─┐...│....│........┌┤........│.......│ ┌┘......┌┘ 027 * └┐...─┤ └─┘ │......─┤ ┌──┐ │......│ │...│ ┌┘.......─┘.......┌┘└─┐....─┬┤.......└┐ │.......│ 028 * ┌┘....└┐ ┌─┐ ┌───┘.......└──┘..└┐ └┐.....│ ┌┘...└┐ ┌─┘.................│ ┌┘.....│└┐.......└─┘.......└┐ 029 * ┌┘......└──┘.└┐┌┘......─┐..........└─┐ ┌┘.....└─┘.....│┌┘...................│ ┌┘......│ │..................│ 030 * ┌┘.............└┘........│............└┬─┘..│...........└┘.....│.........───..└┬┘.......│ │....┌┐............│ 031 * │........................│......┌─┐....│....├┐...............──┘...............│........└─┘....│└┐...........│ 032 * │...┌─┐........................┌┘ └┐.......┌┘└┐................................................│ └┐..........│ 033 * │..┌┘ └┐.....................┌─┘ ┌┘.......│ │...............................................┌┘ │..........│ 034 * └──┘ └┐...................┌┘ ┌┘........│ └─┐...│...─┐...........│........................│ └┐.........│ 035 * │..................┌┘ │.........│ ┌─┐└───┴┐...│...........├─┐........│.............└─┐ │.........│ 036 * ┌─┐ └──┐.....┌──┐......│ ┌┘...┌───┐.└─┘.└┐ └┐..├┐..........│ └┐......┌┤...............│ └─┐.......│ 037 * │.└─┐ └┐...┌┘ └┐.....└┐ │....│ ┌┘......└┐ │..│└───┐.....┌┘ └┐....┌┘├─.....┌──┐.....│ │......┌┘ 038 * │...└┐ └┐..│ ┌┘......└─┬─┘...─┤ │........└┐ ┌─┘.─┤ │.....│ │....│ │......└┐ └┐....│ └┐....─┤ 039 * └┐...│ ┌┘..│ │.........│......│ ┌┘.........└─┘....└─┐ ┌┘.....│ ┌┘....├─┘.......└┐ │....│ │.....│ 040 * └┐..│ ┌┘...│ │................│┌┘...................│ │......└───┘.....│..........│ │....└─┐ │...┌─┘ 041 * │..├─┘...─┤ ┌┘................├┘....................└─┘................│..........└┬┘......└─┐┌┘...│ 042 * ├─.│......│ ┌┘..<........│...#.│...........................┌┬─......................│.........└┘...┌┘ 043 * │..│......│┌┘............│................................─┴┘......................................│ 044 * ┌┘......┌──┘│....................┌─┐.....................................┌┐.........................│ 045 * │....┌──┘ └─┐..................│ └─┐.┌─┐....┌─┐....┌─┐.........┌─┐.....│└─┐......┌─┐....┌──┐......│ 046 * │...┌┘ └─┐...........┌────┘ └─┘┌┴─┬──┘ └────┘ │........┌┘ │....┌┘ │.....┌┘ └────┘ └──┐...│ 047 * │..┌┘ └┐.....┌─┐..└┐ │..│ ┌──┐ └┐......┌┘ ┌┘....└┐ └┐....│ ┌────┐ │...└┐ 048 * ┌┘..└┐ ┌─────┐ ┌┘....┌┘ └┐..└┐ ┌┘..│ ┌─┘..└─┐ └┐.....│ │......└───┘....└┐ ┌┘....└┐ └┐...│ 049 * ┌┘....└─┘.....└┐ ┌┘.....└┐ ┌┘...└┐ │...│┌┘......│ │.....│ └┐..........#....│ ┌┘......└┐ │...│ 050 * ┌┘..........│...└┬┘.......└┐│.....└─┐ ┌─┘...││.......├─┐ │.....│ │...............└─┘........└┐ └┐..└┐ 051 * ┌┘.......┌───┴┐...│.........││.......│ ┌┘....┌┘│.......│.└──┼┬─...│ ┌┘...........................└┐ │...│ 052 * ┌┘.......┌┘ │.............└┘.......└┐ │...┌─┘ │...│.#......└┘....│ ┌┘........................│....└─┴─..└┐ 053 * │........│ ┌┘.......................│ │..┌┘ └┐..│..............└─┘......──.................│...........│ 054 * │........│ ┌┘..........┌─┐..........┌┘ │..│ └┐.............................┌─┐........................└┐ 055 * │........│ ┌┘.....┌─────┘ └──┐.......│ │..│ │.........................┌───┘ ├─...┌────┐...............│ 056 * │........│ │.....┌┘ └┐.....┌┘ └┐.└┐ └┐......┌─┬─┐....┌──┐.....│ ┌┘...┌┘ │....┌─┐........│ 057 * └┐......┌┘ │.....│ ┌────┐ │.....│ ┌┘..└┐ └┐....┌┘┌┘.│....│ └┐....│ ┌┘....│ │....│ │........│ 058 * │....┌─┘ │.....└┐ ┌─┘....│ └┐...─┤ ┌┘....│ └┐...└┬┘.......│ │....└┐ ┌┘.....│ ┌──┘....└┬┘........│ 059 * │....└─┐ └┐.....└─┘......└┐ │....└──┘.....└─┐ ┌─┬┘....│........└┐ ┌┘.....└┐│......└──┘........│.........│ 060 * └┐.....└┐ ┌─┴─..............│ ┌┘...............└──┘.│...............│┌┘.......└┘......................┌┐....│ 061 * │......└─┘.................└┐│.....................................└┘...............................─┤└┐...│ 062 * ┌┘.........│.................└┘.........................│................┌──┐.#.#.....................│ └┐.┌┘ 063 * │..........│..........................................──┼──┐.....#......┌┘ │....................│....└┐ ├─┤ 064 * └┐.........│.....................┌─┐....................│ └─┐..........│ └─┐................#.├─┐...└─┘.└┐ 065 * │....┌┐.........┌─┐.........│..┌┘ └─┐..................│ │...┌───┐..└┐ │..................│ │........│ 066 * └┐...│└─┐.....┌─┘ └┐......┌─┴┐.└┐ └─┐..........│.....│ │...└┐ └┐..└─┐ └─┐....┌──┐.......┌┘ └┐.......│ 067 * ┌┘..┌┘ └┐...┌┘ └┐.....│ │..│ ┌─┐ └┐.......┌─┤....┌┘ └┐...└─┬─┘....└─┐ └┐...└┐ └┐......│ │......┌┘ 068 * │...│ │...└───┐ │.....└┐┌┘..│┌┘.└┐ └┐.....┌┘ │....│ ├─....│........│ └┐...└┐ └┐.....└┐ ┌┘......│ 069 * ┌┘..┌┘ ┌┘.......│ │......└┘...││...└┐ │.....└┐┌┘....└┐┌────┘.........┌─┐..└┐ └┐...└┐ └┐.....└─┘.......└┐ 070 * ┌┘..┌┘ │........│ └┐..........└┘....└─┘......└┘......└┘..............│ └┐..└┐ │....│ ┌┘................│ 071 * │...│ ┌┘....┌───┘ └┐...............................................─┤ └┐..└┐ └┐...└─┤.................│ 072 * │..┌┘ ┌┘....┌┘ │..........................................┌┐....│ │...└┐ │.....│.................│ 073 * │.┌┘ ┌┘.....└┐ │..........................................│└─┐.┌┘ ┌┘....└─┐│.......................│ 074 * │.└┐ ┌┘.......└───┐ ┌┘.......┌┐......┌─┐..................│.....│ └─┘ │.......└┘.....>.........#.......│ 075 * │..└┬─┘............│┌──┘......┌─┘└┐.....│ └┐..─┐.............│.....│ │.............┌─┐...............┌┘ 076 * │...│..┌──┐........││........┌┘ └┐...┌┘ └┐..├───┐..............┌┘ │..┌─┐......┌─┘ ├─....┌┐........│ 077 * │......│ └┐......┌┘└┐......┌┘ └───┘ └──┘ └┐....┌──┐....┌┘ ┌─┴─┬┘ │.....┌┘ │....┌┘└┐......┌┘ 078 * └─┐....│ ├─.....└┐ │......└┐ ┌──┐ │....│ └┐..┌┘ ┌┘...└┐ │.....│ ┌┘....│ └┐.....│ 079 * ├─..┌┘ ┌┘.......│ └┐......└┐ ┌┘..│ └┐...│ └──┘ ┌┘.....└─┘.....└─┐┌┘.....└┐ │.....│ 080 * ┌─┘...└┐ ┌┘........└┐ │.......└───┘...│ │..┌┘ │................└┘.......│ └┐....└─┐ 081 * │......└─┘..........│ └─┐.............└─┬───┐ ┌┘.┌┘ │.........................└┐ ┌┘......│ 082 * │...................└┐ └───┐...........│...└───────┘..└┐ │.......┌─┐................│ │.......│ 083 * └┐...................│ │..........┌┴┐..............└┐ ├─......│ └┐...............└─┘.......│ 084 * └┐.#................│ │..........│ └┐..............└┐ ┌───┘.....┌─┘ │.........................│ 085 * └┐...............┌─┘ │.........┌┘ └┐.....┌───┐....│ │.........│ │......│....┌──────┐.....┌┘ 086 * └┐.┌───┐........│ └───┐....┌┘ └─┐.┌─┘ └─┐.┌┘ └┐......┌─┘ └─┐...┌┴────┘ └─┐..┌┘ 087 * └─┘ └────────┘ └────┘ └─┘ └─┘ └──────┘ └───┘ └──┘ 088 * } 089 * Created by Tommy Ettinger on 8/18/2017. 090 */ 091public class FlowingCaveGenerator implements IDungeonGenerator { 092 public DungeonBoneGen gen; 093 public final int width; 094 public final int height; 095 public TilesetType type; 096 public IRNG rng; 097 public final int[][] environment; 098 private boolean remakeEnvironment = true; 099 protected CellularAutomaton ca; 100 101 /** 102 * Default constructor that makes a 60x60 cave map with a random seed. 103 */ 104 public FlowingCaveGenerator() 105 { 106 this(60, 60); 107 } 108 109 /** 110 * Makes a cave map with the specified dimensions and a random seed. 111 * @param width the width of the dungeon map(s) to generate 112 * @param height the height of the dungeon map(s) to generate 113 */ 114 public FlowingCaveGenerator(int width, int height) { 115 this.width = Math.max(3, width); 116 this.height = Math.max(3, height); 117 type = TilesetType.DEFAULT_DUNGEON; 118 rng = new GWTRNG(); 119 gen = new DungeonBoneGen(rng); 120 ca = new CellularAutomaton(this.width, this.height); 121 environment = new int[this.width][this.height]; 122 } 123 124 /** 125 * 126 * @param width the width of the dungeon map(s) to generate 127 * @param height the height of the dungeon map(s) to generate 128 * @param type a TilesetType enum value; {@link TilesetType#DEFAULT_DUNGEON} is used if null or unspecified 129 * @param rng a random number generator to use when generating the caves; if null this will use a default RNG 130 */ 131 public FlowingCaveGenerator(int width, int height, TilesetType type, IRNG rng) { 132 this.width = Math.max(3, width); 133 this.height = Math.max(3, height); 134 this.type = type == null ? TilesetType.DEFAULT_DUNGEON : type; 135 this.rng = rng == null ? new GWTRNG() : rng; 136 gen = new DungeonBoneGen(this.rng); 137 ca = new CellularAutomaton(this.width, this.height); 138 environment = new int[this.width][this.height]; 139 } 140 141 /** 142 * Generates a flowing cave dungeon withthe same {@link TilesetType} this was made with, or 143 * {@link TilesetType#DEFAULT_DUNGEON} if none was specified. This uses the 144 * convention of '#' representing a wall and '.' representing a bare floor. 145 * 146 * @return a 2D char array representing a cave system with '#' for walls and '.' for floors 147 */ 148 @Override 149 public char[][] generate() { 150 return generate(type); 151 } 152 153 /** 154 * Generates a flowing cave dungeon with a different {@link TilesetType} than this generator was made with. 155 * The default type is {@link TilesetType#DEFAULT_DUNGEON} if unspecified in the constructor. 156 * @param type a TilesetType enum value 157 * @return a 2D char array for the cave system 158 */ 159 public char[][] generate(TilesetType type) { 160 remakeEnvironment = true; 161 gen.generate(type, width, height); 162 ca.remake(gen.region); 163 gen.region.and(ca.runBasicSmoothing()).deteriorate(rng, 0.9); 164 gen.region.and(ca.runBasicSmoothing()).deteriorate(rng, 0.9); 165 ca.current.remake(gen.region.deteriorate(rng, 0.9)); 166 gen.region.or(ca.runBasicSmoothing()); 167 ca.current.remake(gen.region.removeEdges().largestPart()); 168 gen.region.remake(ca.runDiagonalGapCleanup()); 169 return gen.region.intoChars(gen.getDungeon(), '.', '#'); 170 } 171 172 /** 173 * Generates a flowing cave dungeon with a different {@link TilesetType} than this generator was made with, and 174 * specifying a chance to keep the original walls of rooms before the flowing smoothing step is performed. 175 * {@code roomChance} can be between 0.0 and 1.0, and if a room (identified with a similar technique to 176 * {@link RoomFinder}, but not using it directly) is randomly selected to be preserved (the probability per room is 177 * roomChance), then most of its walls will be kept in-place, generally with more right angles than the caves will 178 * have. It may be best to keep roomChance above 0.5 if you want the effect to be noticeable. Starting with 179 * {@link TilesetType#DEFAULT_DUNGEON} is a good choice for {@code type}. 180 * @param type a TilesetType enum value 181 * @param roomChance the chance, from 0.0 to 1.0, to preserve each room, keeping its walls where they start 182 * @return a 2D char array for the cave system 183 */ 184 public char[][] generate(TilesetType type, double roomChance) { 185 remakeEnvironment = true; 186 gen.generate(type, width, height); 187 ArrayList<GreasedRegion> rooms = gen.region.copy().retract8way().flood8way(gen.region, 1).split(); 188 ca.remake(gen.region); 189 gen.region.and(ca.runBasicSmoothing()).deteriorate(rng, 0.9); 190 gen.region.and(ca.runBasicSmoothing()).deteriorate(rng, 0.9); 191 ca.current.remake(gen.region.deteriorate(rng, 0.9)); 192 gen.region.or(ca.runBasicSmoothing()); 193 for (int i = 0; i < rooms.size(); i++) { 194 if(rng.nextDouble() < roomChance) 195 { 196 gen.region.andNot(rooms.get(i).fringe8way().deteriorate(rng, 0.81)); 197 } 198 } 199 gen.region.remake(gen.region.removeEdges()); 200 gen.region.insertSeveral(DungeonUtility.ensurePath(gen.region.intoChars(gen.getDungeon(), '.', '#'), rng, '.', '#')); 201 ca.current.remake(gen.region.largestPart()); 202 gen.region.remake(ca.runDiagonalGapCleanup()); 203 return gen.region.intoChars(gen.getDungeon(), '.', '#'); 204 } 205 206 /** 207 * Gets the most recently-produced dungeon as a 2D char array, usually produced by calling {@link #generate()} or 208 * some similar method present in a specific implementation. This normally passes a direct reference and not a copy, 209 * so you can normally modify the returned array to propagate changes back into this IDungeonGenerator. 210 * 211 * @return the most recently-produced dungeon/map as a 2D char array 212 */ 213 @Override 214 public char[][] getDungeon() { 215 return gen.getDungeon(); 216 } 217 218 /** 219 * Gets an environment map as a 2D int array that {@link SectionDungeonGenerator} can use along with the normal 220 * 2D char array dungeon map to add dungeon features. This marks cells as either {@link DungeonUtility#UNTOUCHED} 221 * (equal to 0), {@link DungeonUtility#CAVE_FLOOR} (equal to 3), or {@link DungeonUtility#CAVE_WALL} (equal to 4). 222 * If the environment has not yet been retrieved since generate() was last called, this assigns the environment map 223 * to match the dungeon map; otherwise it uses the cached environment map. 224 * @return a 2D int array that can be used as an environment map with SectionDungeonGenerator. 225 */ 226 public int[][] getEnvironment() 227 { 228 if(remakeEnvironment) 229 { 230 gen.region.writeIntsInto(environment, 3); 231 gen.workingRegion.remake(gen.region).fringe8way().writeIntsInto(environment, 4); 232 remakeEnvironment = false; 233 } 234 return environment; 235 } 236}