diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java index 558f992..24c0bb3 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java @@ -4,7 +4,11 @@ import net.simon987.server.GameServer; import net.simon987.server.game.Direction; import net.simon987.server.game.World; +import java.awt.*; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Random; public class VaultDimension { @@ -17,18 +21,153 @@ public class VaultDimension { name = "v" + vaultDoorId + "-"; + /* + * Creates a group of vault worlds and pieces them together with openings. + * For a set number of passes, a random number of vault worlds are added to each world in the + * previous 'layer' of worlds in a random direction. Openings are added to allow movement from a + * layer to the next, meaning that adjacent worlds are not necessarily connected, and one would + * necessarily need to travel through 5 openings to reach the 6th layer, even when that layer is + * less than 5 worlds away from the origin/home vault world (the one containing the exit door). + * + * 1. Create home world (layer 0) + * 2. For each world in the current layer, attach a random number of new worlds + * 3. Repeat the same step for the newly added layer + * 4. Choose a random world from the last layer and create the vault box there (objective) + * + * This process is actually done in 2 passes, in the first pass, worlds are defined + * as a set of coordinates + a list of opening directions, then they are actually generated + */ + + int minLayerCount = 4; + int maxLayerCount = 6; + int minAttachedWorld = 0; + int maxAttachedWorld = 4; //todo cap at 4 to avoid infinite loop + + HashMap> worldLayers = new HashMap<>(); VaultWorldGenerator generator = new VaultWorldGenerator(); + Random random = new Random(); + int layerCount = random.nextInt(maxLayerCount - minLayerCount) + minLayerCount; - ArrayList openings = new ArrayList<>(); - openings.add(Direction.WEST); - openings.add(Direction.SOUTH); - openings.add(Direction.EAST); - openings.add(Direction.NORTH); + //1. Create home world + WorldBluePrint homeWorld = new WorldBluePrint(); + homeWorld.coords.x = 0x7FFF; + homeWorld.coords.y = 0x7FFF; + worldLayers.put(0, new ArrayList<>()); + worldLayers.get(0).add(homeWorld); - World vWorld = generator.generateVaultWorld(0x7FFF, 0x7FFF, openings, name); + //2. For each world in the current layer, attach a random number of new worlds + for (int i = 1; i <= layerCount; i++) { - GameServer.INSTANCE.getGameUniverse().addWorld(vWorld); + worldLayers.put(i, new ArrayList<>()); + + for (WorldBluePrint world : worldLayers.get(i - 1)) { + + int attachedWorlds; + if (i == 1) { + attachedWorlds = 4; // The home world should have 4 attached worlds + } else { + attachedWorlds = random.nextInt(maxAttachedWorld - minAttachedWorld) + minAttachedWorld; + } + + for (int j = 0; j < attachedWorlds; j++) { + + int rDirIndex = random.nextInt(4); + + //Try 4 directions (wrap around 0..3) + for (int attemptCount = 0; attemptCount < 4; attemptCount++) { + Direction rDir = Direction.getDirection(rDirIndex); + + //Don't attach a world at the same spot twice + if (!worldExists(world.coordinatesOf(rDir), worldLayers)) { + WorldBluePrint attachedWorld = world.attachWorld(rDir); + worldLayers.get(i).add(attachedWorld); + } + rDirIndex = (rDirIndex + 1) % 4; + } + } + } + } + + ArrayList lastLayerWorlds = new ArrayList<>(); + + //Generate worlds + for (Integer key : worldLayers.keySet()) { + + ArrayList layer = worldLayers.get(key); + + for (WorldBluePrint bp : layer) { + World vWorld = generator.generateVaultWorld(bp.coords.x, bp.coords.y, bp.openings, name); + GameServer.INSTANCE.getGameUniverse().addWorld(vWorld); + + if (key == layerCount - 1) { + lastLayerWorlds.add(vWorld); + } + } + } + + //4. Choose a random world from the last layer and create the vault box there (objective) + World objectiveWorld = lastLayerWorlds.get(random.nextInt(lastLayerWorlds.size())); + + } + + private boolean worldExists(Point coords, HashMap> worldLayers) { + + //Auto-generated by IntelliJ Idea + return worldLayers.values().stream().flatMap(Collection::stream).anyMatch(bp -> bp.coords.equals(coords)); + } + + private class WorldBluePrint { + + public ArrayList openings = new ArrayList<>(4); + + public Point coords = new Point(); + + /** + * Update the blueprint's openings to allow traveling to the newly attached world + * + * @param direction direction of the world to attach (relative to this one) + * @return The blueprint of the attached world + */ + public WorldBluePrint attachWorld(Direction direction) { + + WorldBluePrint attachedWorld = new WorldBluePrint(); + + switch (direction) { + case NORTH: + openings.add(Direction.NORTH); + attachedWorld.openings.add(Direction.SOUTH); + break; + case EAST: + openings.add(Direction.EAST); + attachedWorld.openings.add(Direction.WEST); + break; + case SOUTH: + openings.add(Direction.SOUTH); + attachedWorld.openings.add(Direction.NORTH); + break; + case WEST: + openings.add(Direction.WEST); + attachedWorld.openings.add(Direction.EAST); + break; + } + + attachedWorld.coords.x = coords.x + direction.dX; + attachedWorld.coords.y = coords.y + direction.dY; + + return attachedWorld; + } + + /** + * Get the coordinates of a world that would be attached to this world + * + * @param direction direction of the attached world + */ + Point coordinatesOf(Direction direction) { + + return new Point(coords.x + direction.dX, coords.y + direction.dY); + + } } }