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}