package org.mafagafogigante.dungeon.game; import org.mafagafogigante.dungeon.game.LocationPreset.Type; import org.mafagafogigante.dungeon.logging.DungeonLogger; import org.mafagafogigante.dungeon.util.Percentage; import org.jetbrains.annotations.NotNull; import java.io.Serializable; import java.util.List; /** * A class that know how to create dungeons. */ class DungeonCreator implements Serializable { private static final Percentage HORIZONTAL_EXPANSION_PROBABILITY = Percentage.fromString("50%"); /** * It is of uttermost importance that we respect this rectangle. * * <p>Currently a 5x1 So that we can make dungeons like this: R=R=R (where R is a room and = is a corridor). */ private static final MinimumBoundingRectangle minimumBoundingRectangle = new MinimumBoundingRectangle(5, 1); private final DungeonDistributor distributor; public DungeonCreator(DungeonDistributor distributor) { this.distributor = distributor; } public static MinimumBoundingRectangle getMinimumBoundingRectangle() { return minimumBoundingRectangle; } private static LocationPreset getRandomLocationPreset(Type type) { LocationPresetStore locationPresetStore = LocationPresetStore.getDefaultLocationPresetStore(); List<LocationPreset> entrancePresets = locationPresetStore.getLocationPresetsByType(type); return Random.select(entrancePresets); } /** * Creates a dungeon placing the entrance at the specified point. The world should not have a location at the * specified point. */ public void createDungeon(@NotNull World world, @NotNull Point entrance) { Point mainRoomPoint = createEntrance(world, entrance); Location mainRoomLocation = createMainRoom(world, mainRoomPoint); finishDungeon(world, mainRoomPoint, mainRoomLocation); } /** * Creates the dungeon's entrance and the necessary stairways. * * <p>Returns the point where the main dungeon room should be. */ private Point createEntrance(@NotNull World world, @NotNull Point entrance) { // The entrance. if (world.alreadyHasLocationAt(entrance)) { throw new IllegalStateException("world has location at the specified entrance."); } Location dungeonEntrance = new Location(getRandomLocationPreset(Type.DUNGEON_ENTRANCE), world, entrance); world.addLocation(dungeonEntrance, entrance); distributor.registerDungeonEntrance(entrance); // The stairway. Point stairwayPoint = new Point(entrance, Direction.DOWN); // Note that all DUNGEON_STAIRWAY presets are blocked towards North, East, South, and West. Location stairwayLocation = new Location(getRandomLocationPreset(Type.DUNGEON_STAIRWAY), world, stairwayPoint); world.addLocation(stairwayLocation, stairwayPoint); return new Point(stairwayPoint, Direction.DOWN); } @NotNull private Location createMainRoom(@NotNull World world, Point mainRoomPoint) { // Note that all DUNGEON_ROOM presets are open on all directions. It is up to the code to properly block them. Location dungeonRoom = new Location(getRandomLocationPreset(Type.DUNGEON_ROOM), world, mainRoomPoint); dungeonRoom.getBlockedEntrances().block(Direction.NORTH); dungeonRoom.getBlockedEntrances().block(Direction.DOWN); dungeonRoom.getBlockedEntrances().block(Direction.SOUTH); world.addLocation(dungeonRoom, mainRoomPoint); // The main room. All dungeons have one. return dungeonRoom; } /** * Finishes the dungeon by randomly deciding to expand it to the east and west. * * <p>If this method does not make a corridor to east or west, it blocks that entrance in the main room to prevent * glitches. */ private void finishDungeon(@NotNull World world, Point mainRoomPoint, Location mainRoomLocation) { // UPDATING THIS LOGIC MAY REQUIRE YOU TO UPDATE THE minimumBoundingRectangle variable. if (Random.roll(HORIZONTAL_EXPANSION_PROBABILITY)) { expandTowards(world, mainRoomPoint, Direction.EAST); } else { mainRoomLocation.getBlockedEntrances().block(Direction.EAST); } if (Random.roll(HORIZONTAL_EXPANSION_PROBABILITY)) { expandTowards(world, mainRoomPoint, Direction.WEST); } else { mainRoomLocation.getBlockedEntrances().block(Direction.WEST); } } private void expandTowards(@NotNull World world, @NotNull Point origin, Direction direction) { Point corridorPoint = new Point(origin, direction); if (world.alreadyHasLocationAt(corridorPoint)) { DungeonLogger.warning("Found an existing location when attempting to expand a Dungeon at " + corridorPoint + "."); } // Note that all DUNGEON_CORRIDOR presets have blocked UP and DOWN. It is up to the code to properly block the rest. Location corridorLocation = new Location(getRandomLocationPreset(Type.DUNGEON_CORRIDOR), world, corridorPoint); corridorLocation.getBlockedEntrances().block(Direction.NORTH); corridorLocation.getBlockedEntrances().block(Direction.SOUTH); world.addLocation(corridorLocation, corridorPoint); Point roomPoint = new Point(corridorPoint, direction); if (world.alreadyHasLocationAt(roomPoint)) { DungeonLogger.warning("Found an existing location when attempting to expand a Dungeon at " + roomPoint + "."); } Location roomLocation = new Location(getRandomLocationPreset(Type.DUNGEON_ROOM), world, roomPoint); roomLocation.getBlockedEntrances().block(Direction.UP); roomLocation.getBlockedEntrances().block(Direction.NORTH); roomLocation.getBlockedEntrances().block(Direction.DOWN); roomLocation.getBlockedEntrances().block(Direction.SOUTH); roomLocation.getBlockedEntrances().block(direction); world.addLocation(roomLocation, roomPoint); } }